[proguard] 01/02: Imported Upstream version 3.2

Emmanuel Bourg ebourg-guest at moszumanska.debian.org
Thu Apr 10 09:00:30 UTC 2014


This is an automated email from the git hooks/post-receive script.

ebourg-guest pushed a commit to annotated tag debian/3.2-1
in repository proguard.

commit 7851c476e9fca2d97f1b0a8d85c45a92687efc8f
Author: Sam Clegg <samo at debian.org>
Date:   Thu Apr 10 10:41:50 2014 +0200

    Imported Upstream version 3.2
---
 README                                             |   54 +
 docs/FAQ.html                                      |  191 ++
 docs/GPL.html                                      |  406 ++++
 docs/GPL_exception.html                            |   44 +
 docs/acknowledgements.html                         |   58 +
 docs/alternatives.html                             |  433 +++++
 docs/checkmark.gif                                 |  Bin 0 -> 63 bytes
 docs/downloads.html                                |  221 +++
 docs/drop1.gif                                     |  Bin 0 -> 803 bytes
 docs/drop2.gif                                     |  Bin 0 -> 620 bytes
 docs/drop3.gif                                     |  Bin 0 -> 175 bytes
 docs/feedback.html                                 |  106 +
 docs/index.html                                    |   54 +
 docs/license.html                                  |   46 +
 docs/luciadlogo.png                                |  Bin 0 -> 2356 bytes
 docs/main.html                                     |   79 +
 docs/manual/ant.html                               |  417 ++++
 docs/manual/examples.html                          |  684 +++++++
 docs/manual/gui.html                               |  418 ++++
 docs/manual/index.html                             |   39 +
 docs/manual/introduction.html                      |  116 ++
 docs/manual/limitations.html                       |   95 +
 docs/manual/refcard.html                           |  305 +++
 docs/manual/retrace/examples.html                  |  336 ++++
 docs/manual/retrace/index.html                     |   25 +
 docs/manual/retrace/introduction.html              |   66 +
 docs/manual/retrace/usage.html                     |   81 +
 docs/manual/sections.html                          |  136 ++
 docs/manual/style.css                              |   81 +
 docs/manual/troubleshooting.html                   |  269 +++
 docs/manual/usage.html                             |  849 +++++++++
 docs/manual/wtk.html                               |   58 +
 docs/quality.html                                  |   37 +
 docs/results.html                                  |  134 ++
 docs/screenshot_console.gif                        |  Bin 0 -> 13199 bytes
 docs/screenshot_console_small.gif                  |  Bin 0 -> 87201 bytes
 docs/screenshot_gui1.gif                           |  Bin 0 -> 38674 bytes
 docs/screenshot_gui2.gif                           |  Bin 0 -> 30691 bytes
 docs/screenshot_gui3.gif                           |  Bin 0 -> 29364 bytes
 docs/screenshot_gui4.gif                           |  Bin 0 -> 30409 bytes
 docs/screenshot_gui5.gif                           |  Bin 0 -> 28754 bytes
 docs/screenshot_gui6.gif                           |  Bin 0 -> 23939 bytes
 docs/screenshot_gui7.gif                           |  Bin 0 -> 29404 bytes
 docs/screenshot_gui8.gif                           |  Bin 0 -> 26259 bytes
 docs/screenshots.html                              |   56 +
 docs/screenshots_gui_small.gif                     |  Bin 0 -> 90544 bytes
 docs/sections.html                                 |  121 ++
 docs/sflogo.png                                    |  Bin 0 -> 1299 bytes
 docs/steel.gif                                     |  Bin 0 -> 2759 bytes
 docs/style.css                                     |  128 ++
 docs/testimonials.html                             |   98 +
 docs/title.gif                                     |  Bin 0 -> 2613 bytes
 docs/title.html                                    |   19 +
 docs/vtitle.gif                                    |  Bin 0 -> 22053 bytes
 examples/ant/applets.xml                           |   74 +
 examples/ant/applications1.xml                     |   15 +
 examples/ant/applications2.xml                     |   66 +
 examples/ant/applications3.xml                     |   85 +
 examples/ant/library.xml                           |   95 +
 examples/ant/midlets.xml                           |   44 +
 examples/ant/proguard.xml                          |   62 +
 examples/ant/servlets.xml                          |   74 +
 examples/applets.pro                               |   57 +
 examples/applications.pro                          |   63 +
 examples/dictionaries/compact.txt                  |   19 +
 examples/dictionaries/keywords.txt                 |   58 +
 examples/dictionaries/shakespeare.txt              |   23 +
 examples/library.pro                               |   74 +
 examples/midlets.pro                               |   53 +
 examples/proguard.pro                              |   55 +
 examples/proguardall.pro                           |   67 +
 examples/proguardgui.pro                           |   48 +
 examples/retrace.pro                               |   41 +
 examples/servlets.pro                              |   58 +
 src/proguard/ArgumentWordReader.java               |   83 +
 src/proguard/ClassMemberSpecification.java         |  103 +
 src/proguard/ClassPath.java                        |   94 +
 src/proguard/ClassPathEntry.java                   |  157 ++
 src/proguard/ClassSpecification.java               |  224 +++
 src/proguard/ClassSpecificationVisitorFactory.java |  358 ++++
 src/proguard/Configuration.java                    |  202 ++
 src/proguard/ConfigurationConstants.java           |   94 +
 src/proguard/ConfigurationParser.java              |  919 +++++++++
 src/proguard/ConfigurationWriter.java              |  418 ++++
 src/proguard/DataEntryReaderFactory.java           |  138 ++
 src/proguard/DataEntryWriterFactory.java           |  152 ++
 src/proguard/FileWordReader.java                   |   87 +
 src/proguard/ParseException.java                   |   51 +
 src/proguard/ProGuard.java                         |  936 +++++++++
 src/proguard/SubclassedClassFileFilter.java        |   63 +
 src/proguard/WordReader.java                       |  271 +++
 .../ant/ClassMemberSpecificationElement.java       |  201 ++
 src/proguard/ant/ClassPathElement.java             |  173 ++
 src/proguard/ant/ClassSpecificationElement.java    |  230 +++
 src/proguard/ant/ConfigurationElement.java         |   59 +
 src/proguard/ant/ConfigurationTask.java            |  290 +++
 src/proguard/ant/KeepAttributeElement.java         |   70 +
 src/proguard/ant/ProGuardTask.java                 |  216 +++
 src/proguard/ant/package.html                      |    3 +
 src/proguard/ant/task.properties                   |    2 +
 src/proguard/classfile/ClassConstants.java         |  203 ++
 src/proguard/classfile/ClassCpInfo.java            |  123 ++
 src/proguard/classfile/ClassFile.java              |  188 ++
 src/proguard/classfile/ClassPool.java              |  151 ++
 src/proguard/classfile/CpInfo.java                 |  166 ++
 src/proguard/classfile/DoubleCpInfo.java           |   97 +
 src/proguard/classfile/FieldInfo.java              |   33 +
 src/proguard/classfile/FieldrefCpInfo.java         |   69 +
 src/proguard/classfile/FloatCpInfo.java            |   92 +
 src/proguard/classfile/IntegerCpInfo.java          |   92 +
 .../classfile/InterfaceMethodrefCpInfo.java        |   69 +
 src/proguard/classfile/LibraryClassFile.java       |  586 ++++++
 src/proguard/classfile/LibraryFieldInfo.java       |   60 +
 src/proguard/classfile/LibraryMemberInfo.java      |  129 ++
 src/proguard/classfile/LibraryMethodInfo.java      |   60 +
 src/proguard/classfile/LongCpInfo.java             |   96 +
 src/proguard/classfile/MemberInfo.java             |   53 +
 src/proguard/classfile/MethodInfo.java             |   33 +
 src/proguard/classfile/MethodrefCpInfo.java        |   69 +
 src/proguard/classfile/NameAndTypeCpInfo.java      |  162 ++
 src/proguard/classfile/ProgramClassFile.java       |  537 ++++++
 src/proguard/classfile/ProgramFieldInfo.java       |   70 +
 src/proguard/classfile/ProgramMemberInfo.java      |  208 ++
 src/proguard/classfile/ProgramMethodInfo.java      |   70 +
 src/proguard/classfile/RefCpInfo.java              |  146 ++
 src/proguard/classfile/StringCpInfo.java           |  108 ++
 src/proguard/classfile/Utf8CpInfo.java             |  215 +++
 src/proguard/classfile/VisitorAccepter.java        |   48 +
 .../classfile/attribute/AllAttrInfoVisitor.java    |   68 +
 src/proguard/classfile/attribute/AttrInfo.java     |  184 ++
 .../classfile/attribute/AttrInfoVisitor.java       |   59 +
 src/proguard/classfile/attribute/CodeAttrInfo.java |  190 ++
 .../classfile/attribute/ConstantValueAttrInfo.java |   70 +
 .../classfile/attribute/DeprecatedAttrInfo.java    |   65 +
 .../attribute/EnclosingMethodAttrInfo.java         |  135 ++
 .../classfile/attribute/ExceptionInfo.java         |   91 +
 .../classfile/attribute/ExceptionInfoVisitor.java  |   36 +
 .../classfile/attribute/ExceptionsAttrInfo.java    |   99 +
 .../classfile/attribute/InnerClassesAttrInfo.java  |  104 +
 .../classfile/attribute/InnerClassesInfo.java      |  112 ++
 .../attribute/InnerClassesInfoVisitor.java         |   38 +
 .../classfile/attribute/LibraryAttrInfo.java       |   45 +
 .../classfile/attribute/LineNumberInfo.java        |   64 +
 .../attribute/LineNumberTableAttrInfo.java         |  116 ++
 .../classfile/attribute/LocalVariableInfo.java     |  112 ++
 .../attribute/LocalVariableInfoVisitor.java        |   38 +
 .../attribute/LocalVariableTableAttrInfo.java      |  102 +
 .../classfile/attribute/LocalVariableTypeInfo.java |  113 ++
 .../attribute/LocalVariableTypeInfoVisitor.java    |   38 +
 .../attribute/LocalVariableTypeTableAttrInfo.java  |  102 +
 .../classfile/attribute/MultiAttrInfoVisitor.java  |  234 +++
 .../classfile/attribute/SignatureAttrInfo.java     |   75 +
 .../classfile/attribute/SourceDirAttrInfo.java     |   69 +
 .../classfile/attribute/SourceFileAttrInfo.java    |   70 +
 .../classfile/attribute/SyntheticAttrInfo.java     |   65 +
 .../classfile/attribute/UnknownAttrInfo.java       |   70 +
 .../classfile/attribute/annotation/Annotation.java |  134 ++
 .../annotation/AnnotationDefaultAttrInfo.java      |   77 +
 .../annotation/AnnotationElementValue.java         |   76 +
 .../attribute/annotation/AnnotationVisitor.java    |   40 +
 .../attribute/annotation/ArrayElementValue.java    |   95 +
 .../attribute/annotation/ClassElementValue.java    |   75 +
 .../attribute/annotation/ConstantElementValue.java |   67 +
 .../attribute/annotation/ElementValue.java         |  147 ++
 .../attribute/annotation/ElementValueVisitor.java  |   40 +
 .../annotation/EnumConstantElementValue.java       |   78 +
 .../annotation/RuntimeAnnotationsAttrInfo.java     |   92 +
 .../RuntimeInvisibleAnnotationsAttrInfo.java       |   44 +
 ...ntimeInvisibleParameterAnnotationsAttrInfo.java |   44 +
 .../RuntimeParameterAnnotationsAttrInfo.java       |  120 ++
 .../RuntimeVisibleAnnotationsAttrInfo.java         |   44 +
 ...RuntimeVisibleParameterAnnotationsAttrInfo.java |   44 +
 .../classfile/attribute/annotation/package.html    |    4 +
 src/proguard/classfile/attribute/package.html      |    3 +
 .../classfile/editor/CodeAttrInfoEditor.java       |  667 +++++++
 .../editor/CodeAttrInfoEditorResetter.java         |   76 +
 .../classfile/editor/ComparableCpInfo.java         |  177 ++
 .../classfile/editor/ConstantPoolEditor.java       |  531 ++++++
 .../classfile/editor/ConstantPoolRemapper.java     |  592 ++++++
 .../classfile/editor/ConstantPoolSorter.java       |  128 ++
 .../classfile/editor/StackSizeUpdater.java         |  357 ++++
 src/proguard/classfile/editor/package.html         |    3 +
 .../instruction/AllInstructionVisitor.java         |   71 +
 .../classfile/instruction/BranchInstruction.java   |  133 ++
 .../classfile/instruction/CpInstruction.java       |  267 +++
 .../classfile/instruction/Instruction.java         |  866 +++++++++
 .../instruction/InstructionConstants.java          |  449 +++++
 .../classfile/instruction/InstructionFactory.java  |  310 +++
 .../classfile/instruction/InstructionVisitor.java  |   41 +
 .../instruction/LookUpSwitchInstruction.java       |  142 ++
 .../instruction/MultiInstructionVisitor.java       |  138 ++
 .../classfile/instruction/SimpleInstruction.java   |  182 ++
 .../instruction/TableSwitchInstruction.java        |  145 ++
 .../classfile/instruction/VariableInstruction.java |  224 +++
 src/proguard/classfile/instruction/package.html    |    9 +
 src/proguard/classfile/package.html                |   15 +
 src/proguard/classfile/util/AccessUtil.java        |   99 +
 .../ClassFileClassForNameReferenceInitializer.java |  260 +++
 .../util/ClassFileHierarchyInitializer.java        |  282 +++
 .../util/ClassFileReferenceInitializer.java        |  523 +++++
 .../classfile/util/ClassForNameChecker.java        |  225 +++
 .../classfile/util/ClassNewInstanceChecker.java    |   97 +
 src/proguard/classfile/util/ClassUtil.java         |  935 +++++++++
 .../classfile/util/DescriptorClassEnumeration.java |  165 ++
 .../classfile/util/ExternalTypeEnumeration.java    |  106 +
 .../classfile/util/InternalTypeEnumeration.java    |  108 ++
 src/proguard/classfile/util/MemberFinder.java      |  236 +++
 src/proguard/classfile/util/package.html           |    3 +
 .../classfile/visitor/AllClassFileVisitor.java     |   47 +
 .../classfile/visitor/AllCpInfoVisitor.java        |   52 +
 .../classfile/visitor/AllFieldVisitor.java         |   55 +
 .../classfile/visitor/AllMemberInfoVisitor.java    |   57 +
 .../classfile/visitor/AllMethodVisitor.java        |   55 +
 .../classfile/visitor/BottomClassFileFilter.java   |   69 +
 .../classfile/visitor/ClassFileAccessFilter.java   |   88 +
 .../classfile/visitor/ClassFileCleaner.java        |  368 ++++
 .../visitor/ClassFileHierarchyTraveler.java        |   91 +
 .../visitor/ClassFileMemberInfoVisitor.java        |   88 +
 .../classfile/visitor/ClassFileNameFilter.java     |   81 +
 .../classfile/visitor/ClassFilePrinter.java        |  677 +++++++
 .../classfile/visitor/ClassFileVisitor.java        |   36 +
 .../classfile/visitor/ClassPoolFiller.java         |   69 +
 .../classfile/visitor/ClassPoolVisitor.java        |   37 +
 .../visitor/ConcreteClassFileDownTraveler.java     |  100 +
 src/proguard/classfile/visitor/CpInfoVisitor.java  |   45 +
 .../classfile/visitor/LibraryClassFileFilter.java  |   60 +
 .../classfile/visitor/LibraryMemberInfoFilter.java |   73 +
 .../classfile/visitor/LineNumberInfoVisitor.java   |   38 +
 .../classfile/visitor/MemberInfoAccessFilter.java  |  122 ++
 .../visitor/MemberInfoDescriptorFilter.java        |   99 +
 .../classfile/visitor/MemberInfoNameFilter.java    |   99 +
 .../classfile/visitor/MemberInfoVisitor.java       |   40 +
 .../classfile/visitor/MultiClassFileVisitor.java   |   97 +
 .../classfile/visitor/MultiClassPoolVisitor.java   |   88 +
 .../classfile/visitor/MultiMemberInfoVisitor.java  |  113 ++
 .../classfile/visitor/NamedClassFileVisitor.java   |   55 +
 .../classfile/visitor/NamedFieldVisitor.java       |   59 +
 .../classfile/visitor/NamedMethodVisitor.java      |   59 +
 .../classfile/visitor/ProgramClassFileFilter.java  |   60 +
 .../classfile/visitor/ProgramMemberInfoFilter.java |   73 +
 .../visitor/ReferencedClassFileVisitor.java        |  110 ++
 .../classfile/visitor/SimpleClassFilePrinter.java  |  169 ++
 .../visitor/VariableClassFileVisitor.java          |   78 +
 .../visitor/VariableMemberInfoVisitor.java         |   96 +
 src/proguard/classfile/visitor/package.html        |   40 +
 .../gui/ClassMemberSpecificationDialog.java        |  416 ++++
 .../gui/ClassMemberSpecificationsPanel.java        |  268 +++
 src/proguard/gui/ClassPathPanel.java               |  415 ++++
 src/proguard/gui/ClassSpecificationDialog.java     |  414 ++++
 src/proguard/gui/ClassSpecificationsPanel.java     |  191 ++
 src/proguard/gui/ExtensionFileFilter.java          |   78 +
 src/proguard/gui/FilterDialog.java                 |  296 +++
 src/proguard/gui/GUIResources.java                 |   56 +
 src/proguard/gui/GUIResources.properties           |  255 +++
 src/proguard/gui/ListPanel.java                    |  317 +++
 src/proguard/gui/MessageDialogRunnable.java        |   83 +
 src/proguard/gui/ProGuardGUI.java                  | 1420 ++++++++++++++
 src/proguard/gui/ProGuardRunnable.java             |  148 ++
 src/proguard/gui/ReTraceRunnable.java              |  149 ++
 src/proguard/gui/SwingUtil.java                    |   80 +
 src/proguard/gui/TabbedPane.java                   |  229 +++
 src/proguard/gui/TextAreaOutputStream.java         |   74 +
 src/proguard/gui/arrow.gif                         |  Bin 0 -> 112 bytes
 src/proguard/gui/boilerplate.pro                   |  277 +++
 src/proguard/gui/default.pro                       |  193 ++
 src/proguard/gui/package.html                      |    3 +
 src/proguard/gui/splash/BufferedSprite.java        |   85 +
 src/proguard/gui/splash/CircleSprite.java          |   80 +
 src/proguard/gui/splash/ClipSprite.java            |   85 +
 src/proguard/gui/splash/CompositeSprite.java       |   56 +
 src/proguard/gui/splash/ConstantColor.java         |   51 +
 src/proguard/gui/splash/ConstantDouble.java        |   49 +
 src/proguard/gui/splash/ConstantFont.java          |   46 +
 src/proguard/gui/splash/ConstantInt.java           |   49 +
 src/proguard/gui/splash/ConstantString.java        |   49 +
 src/proguard/gui/splash/ConstantTiming.java        |   58 +
 src/proguard/gui/splash/ImageSprite.java           |   76 +
 src/proguard/gui/splash/LinearColor.java           |   72 +
 src/proguard/gui/splash/LinearDouble.java          |   55 +
 src/proguard/gui/splash/LinearInt.java             |   55 +
 src/proguard/gui/splash/LinearTiming.java          |   55 +
 src/proguard/gui/splash/OverrideGraphics2D.java    |  597 ++++++
 src/proguard/gui/splash/RectangleSprite.java       |  114 ++
 src/proguard/gui/splash/SawToothTiming.java        |   53 +
 src/proguard/gui/splash/ShadowedSprite.java        |  102 +
 src/proguard/gui/splash/SineTiming.java            |   53 +
 src/proguard/gui/splash/SmoothTiming.java          |   66 +
 src/proguard/gui/splash/SplashPanel.java           |  217 +++
 src/proguard/gui/splash/Sprite.java                |   41 +
 src/proguard/gui/splash/TextSprite.java            |  102 +
 src/proguard/gui/splash/TimeSwitchSprite.java      |   75 +
 src/proguard/gui/splash/Timing.java                |   34 +
 src/proguard/gui/splash/TypeWriterString.java      |   71 +
 src/proguard/gui/splash/VariableColor.java         |   36 +
 src/proguard/gui/splash/VariableDouble.java        |   34 +
 src/proguard/gui/splash/VariableFont.java          |   36 +
 src/proguard/gui/splash/VariableInt.java           |   34 +
 src/proguard/gui/splash/VariableSizeFont.java      |   65 +
 src/proguard/gui/splash/VariableString.java        |   34 +
 src/proguard/gui/splash/package.html               |    4 +
 src/proguard/gui/vtitle.gif                        |  Bin 0 -> 26541 bytes
 src/proguard/io/CascadingDataEntryWriter.java      |   86 +
 src/proguard/io/ClassFileFilter.java               |   72 +
 src/proguard/io/ClassFileReader.java               |   94 +
 src/proguard/io/ClassFileRewriter.java             |   77 +
 src/proguard/io/DataEntry.java                     |   55 +
 src/proguard/io/DataEntryCopier.java               |  243 +++
 src/proguard/io/DataEntryFilter.java               |   38 +
 src/proguard/io/DataEntryNameFilter.java           |   54 +
 src/proguard/io/DataEntryParentFilter.java         |   51 +
 src/proguard/io/DataEntryPump.java                 |   43 +
 src/proguard/io/DataEntryReader.java               |   39 +
 src/proguard/io/DataEntryWriter.java               |   65 +
 src/proguard/io/DirectoryPump.java                 |   74 +
 src/proguard/io/DirectoryWriter.java               |  145 ++
 src/proguard/io/FileDataEntry.java                 |   92 +
 src/proguard/io/FilteredDataEntryReader.java       |   96 +
 src/proguard/io/FilteredDataEntryWriter.java       |  112 ++
 src/proguard/io/Finisher.java                      |   37 +
 src/proguard/io/JarReader.java                     |   78 +
 src/proguard/io/JarWriter.java                     |  183 ++
 src/proguard/io/ParentDataEntryWriter.java         |   67 +
 src/proguard/io/RenamedDataEntry.java              |   77 +
 src/proguard/io/ZipDataEntry.java                  |   85 +
 src/proguard/io/package.html                       |    4 +
 src/proguard/obfuscate/AttributeShrinker.java      |  160 ++
 src/proguard/obfuscate/AttributeUsageMarker.java   |  401 ++++
 src/proguard/obfuscate/ClassFileObfuscator.java    |  179 ++
 src/proguard/obfuscate/ClassFileRenamer.java       |  716 +++++++
 src/proguard/obfuscate/MappingKeeper.java          |  119 ++
 src/proguard/obfuscate/MappingPrinter.java         |  170 ++
 src/proguard/obfuscate/MappingProcessor.java       |   78 +
 src/proguard/obfuscate/MappingReader.java          |  195 ++
 src/proguard/obfuscate/MemberInfoLinker.java       |  185 ++
 src/proguard/obfuscate/MemberInfoObfuscator.java   |  404 ++++
 src/proguard/obfuscate/MultiMappingProcessor.java  |   98 +
 src/proguard/obfuscate/NameAndTypeShrinker.java    |  124 ++
 src/proguard/obfuscate/NameAndTypeUsageMarker.java |  160 ++
 src/proguard/obfuscate/NameFactory.java            |   36 +
 src/proguard/obfuscate/NameMarker.java             |   78 +
 src/proguard/obfuscate/ReadNameFactory.java        |  171 ++
 src/proguard/obfuscate/SimpleNameFactory.java      |  156 ++
 src/proguard/obfuscate/Utf8Shrinker.java           |  121 ++
 src/proguard/obfuscate/Utf8UsageMarker.java        |  419 ++++
 src/proguard/obfuscate/package.html                |    3 +
 src/proguard/optimize/ChangedCodePrinter.java      |  203 ++
 src/proguard/optimize/KeepMarker.java              |   92 +
 .../optimize/NoSideEffectMethodMarker.java         |   74 +
 .../optimize/SideEffectInstructionChecker.java     |  246 +++
 src/proguard/optimize/SideEffectMethodMarker.java  |  172 ++
 src/proguard/optimize/WriteOnlyFieldMarker.java    |  158 ++
 src/proguard/optimize/evaluation/BranchUnit.java   |   64 +
 .../optimize/evaluation/PartialEvaluator.java      | 2016 ++++++++++++++++++++
 src/proguard/optimize/evaluation/Processor.java    |  997 ++++++++++
 src/proguard/optimize/evaluation/Stack.java        |  515 +++++
 .../optimize/evaluation/TracedBranchUnit.java      |  146 ++
 src/proguard/optimize/evaluation/TracedStack.java  |  336 ++++
 .../optimize/evaluation/TracedVariables.java       |  191 ++
 src/proguard/optimize/evaluation/Variables.java    |  312 +++
 src/proguard/optimize/evaluation/package.html      |    4 +
 .../optimize/evaluation/value/Category1Value.java  |   41 +
 .../optimize/evaluation/value/Category2Value.java  |   41 +
 .../optimize/evaluation/value/DoubleValue.java     |  312 +++
 .../evaluation/value/DoubleValueFactory.java       |   53 +
 .../optimize/evaluation/value/FloatValue.java      |  312 +++
 .../evaluation/value/FloatValueFactory.java        |   55 +
 .../evaluation/value/InstructionOffsetValue.java   |  237 +++
 .../value/InstructionOffsetValueFactory.java       |   59 +
 .../optimize/evaluation/value/IntegerValue.java    |  622 ++++++
 .../evaluation/value/IntegerValueFactory.java      |   66 +
 .../optimize/evaluation/value/LongValue.java       |  390 ++++
 .../evaluation/value/LongValueFactory.java         |   53 +
 .../optimize/evaluation/value/ReferenceValue.java  |  190 ++
 .../evaluation/value/ReferenceValueFactory.java    |   80 +
 .../value/SpecificArrayReferenceValue.java         |  160 ++
 .../evaluation/value/SpecificDoubleValue.java      |  213 +++
 .../evaluation/value/SpecificFloatValue.java       |  213 +++
 .../evaluation/value/SpecificIntegerValue.java     |  388 ++++
 .../evaluation/value/SpecificLongValue.java        |  258 +++
 .../evaluation/value/SpecificReferenceValue.java   |  158 ++
 src/proguard/optimize/evaluation/value/Value.java  |  143 ++
 .../optimize/evaluation/value/ValueFactory.java    |   63 +
 .../optimize/evaluation/value/package.html         |    3 +
 src/proguard/optimize/package.html                 |    4 +
 .../optimize/peephole/BranchTargetFinder.java      |  178 ++
 .../optimize/peephole/ClassFileFinalizer.java      |   96 +
 .../optimize/peephole/GetterSetterInliner.java     |  387 ++++
 .../optimize/peephole/GotoReturnReplacer.java      |   92 +
 .../optimize/peephole/LoadStoreRemover.java        |   99 +
 src/proguard/optimize/peephole/NopRemover.java     |   68 +
 src/proguard/optimize/peephole/PushPopRemover.java |  139 ++
 .../peephole/SingleImplementationInliner.java      |  534 ++++++
 .../peephole/SingleImplementationMarker.java       |  160 ++
 .../optimize/peephole/StoreLoadReplacer.java       |  116 ++
 src/proguard/optimize/peephole/package.html        |    3 +
 src/proguard/package.html                          |    5 +
 src/proguard/retrace/ReTrace.java                  |  155 ++
 src/proguard/retrace/StackTrace.java               |  177 ++
 src/proguard/retrace/StackTraceItem.java           |  294 +++
 src/proguard/retrace/package.html                  |    4 +
 src/proguard/shrink/ClassFileShrinker.java         |  341 ++++
 src/proguard/shrink/InnerUsageMarker.java          |  224 +++
 src/proguard/shrink/InterfaceUsageMarker.java      |  158 ++
 src/proguard/shrink/UsageMarker.java               |  824 ++++++++
 src/proguard/shrink/UsagePrinter.java              |  174 ++
 src/proguard/shrink/UsedClassFileFilter.java       |   65 +
 src/proguard/shrink/package.html                   |    3 +
 src/proguard/util/BasicListMatcher.java            |  148 ++
 src/proguard/util/BasicMatcher.java                |  360 ++++
 src/proguard/util/ClassNameListMatcher.java        |   88 +
 src/proguard/util/ClassNameMatcher.java            |   93 +
 src/proguard/util/ExtensionMatcher.java            |   52 +
 src/proguard/util/FileNameListMatcher.java         |   88 +
 src/proguard/util/FileNameMatcher.java             |   89 +
 src/proguard/util/ListUtil.java                    |   87 +
 src/proguard/util/StringMatcher.java               |   38 +
 src/proguard/util/package.html                     |    3 +
 src/proguard/wtk/ProGuardObfuscator.java           |  138 ++
 src/proguard/wtk/default.pro                       |  112 ++
 src/proguard/wtk/package.html                      |    3 +
 420 files changed, 62137 insertions(+)

diff --git a/README b/README
new file mode 100644
index 0000000..e1cde65
--- /dev/null
+++ b/README
@@ -0,0 +1,54 @@
+ProGuard, Java class file shrinker and obfuscator
+=================================================
+
+This distribution contains the following directories:
+
+- lib      : the main jars, compiled and ready to use with "java -jar ...."
+- docs     : the complete documentation, licenses, etc. in html format
+- examples : some example configuration files
+- src      : the source code
+
+
+The best place to start is docs/index.html
+
+
+Example
+=======
+
+If you want to give ProGuard a spin right away, try processing the jar itself:
+
+    cd lib
+    java -jar proguard.jar @../examples/proguard.pro
+
+The resulting proguard_out.jar contains the same application, but it's much
+smaller!
+
+
+Development
+===========
+
+If you're interested in changing and extending ProGuard, you can start by
+compiling the source code yourself:
+
+    mkdir classes
+    javac -sourcepath src -d classes src/proguard/ProGuard.java
+    javac -sourcepath src -d classes src/proguard/retrace/ReTrace.java
+    javac -sourcepath src -d classes src/proguard/gui/ProGuardGUI.java
+
+If you want to compile the ProGuard Ant task as well:
+
+    javac -sourcepath src -d classes -classpath lib/ant.jar \
+        src/proguard/ant/ProGuardTask.java
+
+If you want to compile the J2ME WTK obfuscator plug-in:
+
+    javac -sourcepath src -d classes -classpath wtklib/kenv.zip \
+        src/proguard/wtk/ProGuardObfuscator.java
+
+Note that you'll have to install Ant and the J2ME WTK yourself.
+
+Enjoy!
+
+http://proguard.sourceforge.net/
+
+Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
diff --git a/docs/FAQ.html b/docs/FAQ.html
new file mode 100644
index 0000000..16660b8
--- /dev/null
+++ b/docs/FAQ.html
@@ -0,0 +1,191 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard FAQ</title>
+</head>
+<body>
+
+<h2>Frequently Asked Questions</h2>
+
+<h3>Contents</h3>
+
+<ol>
+<li><a href="#shrinking">What is shrinking?</a>
+<li><a href="#obfuscation">What is obfuscation?</a>
+<li><a href="#optimization">What kind of optimizations does <b>ProGuard</b>
+    support?</a>
+<li><a href="#jdk1.4">Does <b>ProGuard</b> work with JDK1.4? JDK5.0?</a>
+<li><a href="#j2me">Does <b>ProGuard</b> work with J2ME?</a>
+<li><a href="#ant">Does <b>ProGuard</b> have support for Ant?</a>
+<li><a href="#gui">Does <b>ProGuard</b> come with a GUI?</a>
+<li><a href="#forname">Does <b>ProGuard</b> handle <code>Class.forName</code>
+    calls?</a>
+<li><a href="#resource">Does <b>ProGuard</b> handle resource files?</a>
+<li><a href="#encrypt">Does <b>ProGuard</b> encrypt strings constants?</a>
+<li><a href="#flow">Does <b>ProGuard</b> perform control flow obfuscation?</a>
+<li><a href="#incremental">Does <b>ProGuard</b> support incremental
+    obfuscation?</a>
+<li><a href="#keywords">Can <b>ProGuard</b> obfuscate using reserved
+    keywords?</a>
+<li><a href="#stacktrace">Can <b>ProGuard</b> reconstruct obfuscated stack
+    traces?</a>
+</ol>
+
+<a name="shrinking"> </a>
+<h3>What is shrinking?</h3>
+Java source code (.java files) is typically compiled to bytecode (.class
+files). Complete programs or program libraries are usually zipped up and
+distributed as Java archives (.jar files). Bytecode is more compact than Java
+source code, but it may still contain a lot of unused code, especially if it
+uses program libraries. Shrinking programs such as <b>ProGuard</b> can analyze
+bytecode and remove unused classes, fields, and methods. The program remains
+functionally equivalent, including the information given in exception stack
+traces.
+
+<a name="obfuscation"> </a>
+<h3>What is obfuscation?</h3>
+By default, compiled bytecode still contains a lot of debugging information:
+source file names, line numbers, field names, method names, argument names,
+variable names, etc. This information makes it straightforward to decompile
+the bytecode and reverse-engineer entire programs. Sometimes, this is not
+desirable. Obfuscators such as <b>ProGuard</b> can remove the debugging
+information and replace all names by meaningless character sequences, making
+it much harder to reverse-engineer the code. It further compacts the code as a
+bonus. The program remains functionally equivalent, except for the class
+names, method names, and line numbers given in exception stack traces.
+
+<a name="optimization"> </a>
+<h3>What kind of optimizations does <b>ProGuard</b> support?</h3>
+Apart from removing unused classes, fields, and methods in the shrinking step,
+<b>ProGuard</b> can also perform optimizations at the bytecode level, inside
+methods:
+<ul>
+<li>Evaluating constant expressions.
+<li>Removing unnecessary computations.
+<li>Removing unnecessary field accesses.
+<li>Removing unnecessary method calls.
+<li>Removing unnecessary branches.
+<li>Removing unnecessary comparisons and instanceof tests.
+<li>Removing write-only fields.
+<li>Various peephole optimizations like push/pop simplification.
+<li>Making classes and methods final when possible.
+<li>Inlining simple getters and setters when possible.
+<li>Replacing interfaces that have single implementations.
+<li>Optionally removing logging code.
+</ul>
+The positive effects of these optimizations will depend on your code and on
+the virtual machine on which the code is executed. Simple virtual machines may
+benefit more than advanced virtual machines with sophisticated JIT compilers.
+At the very least, your bytecode may become a bit smaller.
+<p>
+A notable optimization that isn't supported yet is the inlining of constant
+fields that are not marked as final.
+
+<a name="jdk1.4"> </a>
+<h3>Does <b>ProGuard</b> work with JDK1.4? JDK5.0?</h3>
+Yes. Class files compiled with JDK1.4 are targeted at JRE1.2 by default. Class
+files compiled with JDK5.0 may have additional attributes for supporting
+generics and annotations. The compiled class files have slightly different
+structures from older class files. <b>ProGuard</b> handles all versions
+correctly.
+
+<a name="j2me"> </a>
+<h3>Does <b>ProGuard</b> work with J2ME?</h3>
+Yes. <b>ProGuard</b> itself runs in J2SE, but you can freely specify the
+run-time environment at which your programs are targeted, including J2ME. For
+instance, you can specify <code>midpapi20.jar</code> and
+<code>cldcapi11.jar</code> as the run-time libraries, instead of J2SE's
+traditional <code>rt.jar</code>. All of <b>ProGuard</b>'s powerful
+configuration options remain available. The <a
+href="manual/examples.html">example section</a> of the <a
+href="manual/index.html">ProGuard User Manual</a> illustrates how to process
+<a href="manual/examples.html#midlet">a single midlet</a> or <a
+href="manual/examples.html#midlets">all midlets</a> in your input jar, for
+instance.
+<p>
+In addition, <b>ProGuard</b> provides an obfuscator plug-in for the J2ME
+Wireless Toolkit.
+
+<a name="ant"> </a>
+<h3>Does <b>ProGuard</b> have support for Ant?</h3>
+Yes. <b>ProGuard</b> provides an Ant task, so that it integrates seamlessly
+into your Ant build processes. You can still use configurations in
+<b>ProGuard</b>'s own readable format. Alternatively, if you prefer XML, you
+can specify the equivalent XML configuration.
+
+<a name="gui"> </a>
+<h3>Does <b>ProGuard</b> come with a GUI?</h3>
+Yes. First of all, <b>ProGuard</b> is perfectly usable as a command-line tool
+that can easily be integrated into any automatic build process. For casual
+users, there's also a graphical user interface that makes creating, loading,
+editing, executing, and saving ProGuard configurations a breeze.
+
+<a name="forname"> </a>
+<h3>Does <b>ProGuard</b> handle <code>Class.forName</code> calls?</h3>
+Yes. <b>ProGuard</b> automatically handles
+<code>Class.forName("SomeClass")</code> and <code>SomeClass.class</code>
+constructs. The referenced classes are preserved in the shrinking phase, and
+the string arguments are properly replaced in the obfuscation phase.
+<p>
+With variable string arguments, it's generally not possible to determine their
+possible values. They might be read from a configuration file, for instance.
+However, <b>ProGuard</b> will note constructs like
+"<code>(SomeClass)Class.forName(variable).newInstance()</code>". These might
+be an indication that the class or interface <code>SomeClass</code> and/or its
+implementations may need to be preserved. The user can adapt his configuration
+accordingly.
+
+<a name="resource"> </a>
+<h3>Does <b>ProGuard</b> handle resource files?</h3>
+Yes, in the sense that <b>ProGuard</b> copies all non-class resource files
+from the given input jars to the output jars. Their names and contents remain
+unchanged.
+
+<a name="encrypt"> </a>
+<h3>Does <b>ProGuard</b> encrypt strings constants?</h3>
+No. Storing encrypted string constants in program code is fairly futile, since
+the encryption has to be perfectly reversible by definition. Moreover, the
+decryption costs additional memory and computation at run-time. If this feature
+is ever incorporated, I'll provide a tool to decrypt the strings as well.
+
+<a name="flow"> </a>
+<h3>Does <b>ProGuard</b> perform flow obfuscation?</h3>
+No. Control obfuscation injects additional branches into the bytecode, in an
+attempt to fool decompilers. This operation may confuse JIT compilers as well,
+adversely affecting performance. It may be added as an option in the future,
+but it doesn't have the highest priority.
+
+<a name="incremental"> </a>
+<h3>Does <b>ProGuard</b> support incremental obfuscation?</h3>
+Yes. This feature allows you to specify a previous obfuscation mapping file in
+a new obfuscation step, in order to produce add-ons or patches for obfuscated
+code.
+
+<a name="keywords"> </a>
+<h3>Can <b>ProGuard</b> obfuscate using reserved keywords?</h3>
+Yes. You can specify your own obfuscation dictionary, such as a list of
+reserved key words, identifiers with foreign characters, random source files,
+or a text by Shakespeare. Note that this hardly improves the obfuscation.
+Decent decompilers can automatically replace reserved keywords, and the effect
+can fairly simply be undone by obfuscating again with simpler names.
+
+<a name="stacktrace"> </a>
+<h3>Can <b>ProGuard</b> reconstruct obfuscated stack traces?</h3>
+Yes. <b>ProGuard</b> comes with a companion tool, <b>ReTrace</b>, that can
+'de-obfuscate' stack traces produced by obfuscated applications. The
+reconstruction is based on the mapping file that <b>ProGuard</b> can write
+out. If line numbers have been obfuscated away, a list of alternative method
+names is presented for each obfuscated method name that has an ambiguous
+reverse mapping. Please refer to the <a href="manual/index.html">ProGuard User
+Manual</a> for more details.
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
diff --git a/docs/GPL.html b/docs/GPL.html
new file mode 100644
index 0000000..c7a2458
--- /dev/null
+++ b/docs/GPL.html
@@ -0,0 +1,406 @@
+<!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN">
+<HTML>
+<HEAD>
+<TITLE>GNU General Public License</TITLE>
+</HEAD>
+<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#1F00FF" ALINK="#FF0000" VLINK="#9900DD">
+<H1>GNU General Public License</H1>
+<H2>Table of Contents</H2>
+<UL>
+
+  <LI><A NAME="TOC1" HREF="#SEC1">GNU GENERAL PUBLIC LICENSE</A>
+<UL>
+<LI><A NAME="TOC2" HREF="#SEC2">Preamble</A>
+<LI><A NAME="TOC3" HREF="#SEC3">TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</A>
+
+</UL>
+</UL>
+
+<P>
+
+<HR>
+
+<P>
+
+
+
+<H2><A NAME="SEC1" HREF="#TOC1">GNU GENERAL PUBLIC LICENSE</A></H2>
+<P>
+Version 2, June 1991
+
+</P>
+
+<PRE>
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+</PRE>
+
+
+
+<H2><A NAME="SEC2" HREF="#TOC2">Preamble</A></H2>
+
+<P>
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+</P>
+<P>
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+</P>
+<P>
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+</P>
+<P>
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+</P>
+<P>
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+</P>
+<P>
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+</P>
+<P>
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+</P>
+<P>
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+</P>
+
+
+<H2><A NAME="SEC3" HREF="#TOC3">TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</A></H2>
+
+
+<P>
+
+<STRONG>0.</STRONG>
+ This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+<P>
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+<P>
+
+<STRONG>1.</STRONG>
+ You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+<P>
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+<P>
+
+<STRONG>2.</STRONG>
+ You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+<P>
+
+<UL>
+
+<LI><STRONG>a)</STRONG>
+     You must cause the modified files to carry prominent notices
+     stating that you changed the files and the date of any change.
+
+<P>
+<LI><STRONG>b)</STRONG>
+     You must cause any work that you distribute or publish, that in
+     whole or in part contains or is derived from the Program or any
+     part thereof, to be licensed as a whole at no charge to all third
+     parties under the terms of this License.
+
+<P>
+<LI><STRONG>c)</STRONG>
+     If the modified program normally reads commands interactively
+     when run, you must cause it, when started running for such
+     interactive use in the most ordinary way, to print or display an
+     announcement including an appropriate copyright notice and a
+     notice that there is no warranty (or else, saying that you provide
+     a warranty) and that users may redistribute the program under
+     these conditions, and telling the user how to view a copy of this
+     License.  (Exception: if the Program itself is interactive but
+     does not normally print such an announcement, your work based on
+     the Program is not required to print an announcement.)
+</UL>
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+<P>
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+<P>
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+<P>
+
+<STRONG>3.</STRONG>
+ You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+
+<!-- we use this doubled UL to get the sub-sections indented, -->
+<!-- while making the bullets as unobvious as possible. -->
+<UL>
+
+<LI><STRONG>a)</STRONG>
+     Accompany it with the complete corresponding machine-readable
+     source code, which must be distributed under the terms of Sections
+     1 and 2 above on a medium customarily used for software interchange; or,
+
+<P>
+<LI><STRONG>b)</STRONG>
+     Accompany it with a written offer, valid for at least three
+     years, to give any third party, for a charge no more than your
+     cost of physically performing source distribution, a complete
+     machine-readable copy of the corresponding source code, to be
+     distributed under the terms of Sections 1 and 2 above on a medium
+     customarily used for software interchange; or,
+
+<P>
+<LI><STRONG>c)</STRONG>
+     Accompany it with the information you received as to the offer
+     to distribute corresponding source code.  (This alternative is
+     allowed only for noncommercial distribution and only if you
+     received the program in object code or executable form with such
+     an offer, in accord with Subsection b above.)
+</UL>
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+<P>
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+<P>
+
+<STRONG>4.</STRONG>
+ You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+<P>
+
+<STRONG>5.</STRONG>
+ You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+<P>
+
+<STRONG>6.</STRONG>
+ Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+<P>
+
+<STRONG>7.</STRONG>
+ If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+<P>
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+<P>
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+<P>
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+<P>
+
+<STRONG>8.</STRONG>
+ If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+<P>
+
+<STRONG>9.</STRONG>
+ The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+<P>
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+<P>
+
+
+<STRONG>10.</STRONG>
+ If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+
+
+<P><STRONG>NO WARRANTY</STRONG></P>
+
+<P>
+
+<STRONG>11.</STRONG>
+ BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+<P>
+
+<STRONG>12.</STRONG>
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+<P>
+
+
+<H2>END OF TERMS AND CONDITIONS</H2>
+</BODY>
+</HTML>
diff --git a/docs/GPL_exception.html b/docs/GPL_exception.html
new file mode 100644
index 0000000..3d136dc
--- /dev/null
+++ b/docs/GPL_exception.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN">
+<HTML>
+<HEAD>
+<TITLE>Special Exception to the GNU General Public License</TITLE>
+</HEAD>
+<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#1F00FF" ALINK="#FF0000" VLINK="#9900DD">
+<H1>Special Exception to the GNU General Public License</H1>
+
+<P>
+Copyright © 2002-2004 Eric Lafortune
+</P>
+
+<P>
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+</P>
+
+<P>
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+</P>
+
+<P>
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place, Suite 330, Boston, MA 02111-1307 USA
+</P>
+
+<P>
+In addition, as a special exception, Eric Lafortune gives permission to link
+the code of this program with Apache Ant, the Sun J2ME Wireless Toolkit, and
+the Eclipse Platform, and distribute linked combinations including the two.
+You must obey the GNU General Public License in all respects for all of the
+code used other than these programs. If you modify this file, you may extend
+this exception to your version of the file, but you are not obligated to do
+so. If you do not wish to do so, delete this exception statement from your
+version.
+</P>
+
+</BODY>
+</HTML>
diff --git a/docs/acknowledgements.html b/docs/acknowledgements.html
new file mode 100644
index 0000000..71bd57c
--- /dev/null
+++ b/docs/acknowledgements.html
@@ -0,0 +1,58 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard Acknowledgements</title>
+</head>
+<body>
+
+<h2>Acknowledgements</h2>
+
+<b>ProGuard</b> grew out of <b>RetroGuard</b>, which its author Mark Welsh has
+kindly made available under the GNU Lesser General Public License.
+<b>RetroGuard</b> is a very nice piece of code, but it only performs
+obfuscation. I started from the class file parsing code and wrote my own
+shrinker, optimizer, and obfuscator. At this point, both programs have little
+code in common.
+<p>
+
+Dirk Schnelle has generously contributed and maintained the first versions of
+the Ant task. The implementation has been rewritten for version 3.0, but the
+XML schema is still based on his work.
+<p>
+
+I am developing ProGuard in my spare time, in part on equipment that my
+employer <a href="http://www.luciad.com/" target="other">Luciad</a> is kindly
+allowing me to use.
+<p>
+
+<a href="http://sourceforge.net/forum/projects/proguard/"
+target="other">SourceForge</a> is graciously providing the resources for
+hosting this project and many other projects.
+<p>
+
+My colleagues at Luciad have been very patient trying early versions of the
+code. Since the first public release, others have chimed in with interesting
+ideas, bug reports, and bug fixes: Thorsten Heit, Oliver Retzl, Jonathan
+Knudsen, Bob Drury, Dave Jarvis, Marc Chapman, Dave Morehouse, Richard
+Osbaldeston, Peter Hawkins, Mark Sherington, David Sitsky, James Manning,
+Ptolemy Oberin, Frank-Michael Moser, QZ Shines, Thomas Singer, Michele
+Puccini, Roman Bednarek, Natalia Pujol, Daniel Sjöblom, Jan Filipsky,
+Charles Smith, and Gerrit Telkamp.
+Thanks! Your feedback has been invaluable.
+<p>
+
+The code and these web pages were written using Sun's JDKs, IBM Eclipse, Linux,
+GNU emacs, bash, sed, awk, and a whole host of other free tools which continue
+to make programming interesting.
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+
+</body>
+</html>
diff --git a/docs/alternatives.html b/docs/alternatives.html
new file mode 100644
index 0000000..6918ecb
--- /dev/null
+++ b/docs/alternatives.html
@@ -0,0 +1,433 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard Alternatives</title>
+</head>
+<body>
+
+<h2>Alternatives</h2>
+
+There are quite a few Java class file shrinkers, optimizers, and obfuscators
+out there. Users of <b>ProGuard</b> tell me it compares with the best of them.
+However, you may want to check that out yourself.
+<p>
+This is a list of the programs of which I'm aware. Obviously, I've never
+personally tested all of them. Many programs, even commercial ones, have been
+abandoned. Please drop me a note if you know of any other shrinkers,
+optimizers, or obfuscators, or if some information provided below is
+incorrect.
+<p>
+
+<table>
+
+<tr>
+<th>Author/Company</th>
+<th>Program</th>
+<th>Shrinking</th>
+<th>Optimization</th>
+<th>Obfuscation</th>
+<th>License</th>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a></td>
+<td><a target="other" href="http://proguard.sourceforge.net/">ProGuard</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Free (GPL)</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.informatik.uni-oldenburg.de/leute/hoenicke.html">Jochen Hoenicke</a></td>
+<td><a target="other" href="http://jode.sourceforge.net/">Jode</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Free (GPL)</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.nq4.de/">NQ4</a></td>
+<td><a target="other" href="http://www.nq4.de/">Joga</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Free (no source)</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://sourceforge.net/users/hchacha/">Hidetoshi Ohuchi</a></td>
+<td><a target="other" href="http://jarg.sourceforge.net/">Jarg</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Free (BSD)</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.geocities.com/CapeCanaveral/Hall/2334/resume.html">Alexander Shvets</a></td>
+<td><a target="other" href="http://www.geocities.com/CapeCanaveral/Hall/2334/Programs/cafebabe.html">CafeBabe</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Free</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.cs.cornell.edu/nystrom/">Nate Nystrom</a></td>
+<td><a target="other" href="http://www.cs.purdue.edu/homes/hosking/bloat/">Bloat</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
+<td>Free</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.cs.purdue.edu/homes/grothoff/">Christian Grothoff</a></td>
+<td><a target="other" href="http://www.ovmj.org/jamit/">Jamit</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td>Free (GPL)</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.riggshill.com/">RiggsHill Software</a></td>
+<td><a target="other" href="http://genjar.sourceforge.net/">GenJar</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td>Free (Apache)</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.sable.mcgill.ca/">Sable</a></td>
+<td><a target="other" href="http://www.sable.mcgill.ca/soot/">Soot</a></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
+<td>Free (LGPL)</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.utdallas.edu/~gxz014000/">Bajie</a></td>
+<td><a target="other" href="http://www.utdallas.edu/~gxz014000/jcmp/">JCMP</a></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Free</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.retrologic.com/">RetroLogic</a></td>
+<td><a target="other" href="http://www.retrologic.com/">RetroGuard</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Free (LGPL)</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://sourceforge.net/users/glurk/">Thorsten Heit</a></td>
+<td><a target="other" href="http://sourceforge.net/projects/javaguard/">JavaGuard</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Free (LGPL)</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://mwobfu.sourceforge.net/">Patrick Mueller</a></td>
+<td><a target="other" href="http://mwobfu.sourceforge.net/">Mwobfu</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Free (GPL)</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.yworks.com/">yWorks</a></td>
+<td><a target="other" href="http://www.yworks.com/en/products_yguard_about.htm">yGuard</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Free</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.drjava.de/">Dr. Java</a></td>
+<td><a target="other" href="http://www.drjava.de/obfuscator/">Marvin Obfuscator</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Free (no source)</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.alphaworks.ibm.com/">IBM AlphaWorks</a></td>
+<td><a target="other" href="http://www.alphaworks.ibm.com/tech/jax/">JAX</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.preemptive.com/">PreEmptive</a></td>
+<td><a target="other" href="http://www.preemptive.com/products.html">DashOPro</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.zelix.com/">Zelix</a></td>
+<td><a target="other" href="http://www.zelix.com/klassmaster/index.html">KlassMaster</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.s5systems.com/">S5 Systems</a></td>
+<td><a target="other" href="http://www.s5systems.com/jPresto.htm">jPresto</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.codingart.com/">CodingArt</a></td>
+<td><a target="other" href="http://www.codingart.com/codeshield.html">CodeShield</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.e-t.com/">Eastridge Technology</a></td>
+<td><a target="other" href="http://www.e-t.com/jshrink.html">Jshrink</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.helseth.com/">Helseth</a></td>
+<td><a target="other" href="http://www.helseth.com/HJO.htm">JObfuscator</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.leesw.com/">LeeSoftware</a></td>
+<td><a target="other" href="http://www.leesw.com/">Smokescreen Obfuscator</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.vegatech.com/">Vega Technologies</a></td>
+<td><a target="other" href="http://www.vegatech.com/jzipper/">JZipper</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.uni-vologda.ac.ru/~c3c/">Sergey Sverdlov</a></td>
+<td><a target="other" href="http://www.uni-vologda.ac.ru/~c3c/jco/">J.Class Optimizer</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://cs.arizona.edu/">U. of Arizona</a></td>
+<td><a target="other" href="http://sandmark.cs.arizona.edu/">SandMark</a></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.force5.com/">Force 5</a></td>
+<td><a target="other" href="http://www.force5.com/">JCloak</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.wingsoft.com/">WingSoft</a></td>
+<td><a target="other" href="http://www.wingsoft.com/wingguard.html">WingGuard</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.jammconsulting.com/">JAMM Consulting</a></td>
+<td><a target="other" href="http://www.jammconsulting.com/jamm/servlet/com.jammconsulting.servlet.JAMMServlet?pageId=ObfuscateProPage">ObfuscatePro</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.2lkit.com/">2LKit</a></td>
+<td><a target="other" href="http://www.2lkit.com/products/2LKitObf/index.htm">2LKit Obfuscator</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.duckware.com/">Duckware</a></td>
+<td><a target="other" href="http://www.duckware.com/jobfuscate/">Jobfuscate</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.jproof.com/">JProof</a></td>
+<td><a target="other" href="http://www.jproof.com/">JProof</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.4fang.net/">4Fang</a></td>
+<td><a target="other" href="http://www.4fang.net/jmix/">JMix</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.solutia.ro/">GITS</a></td>
+<td><a target="other" href="http://www.solutia.ro/pages/javadc/">Blurfuscator</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.jdevelop.com/">JDevelop</a></td>
+<td><a target="other" href="http://www.jdevelop.com/best-java-obfuscator.html">JSCO</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://sourceforge.net/projects/flmobf/">Alain Moran</a></td>
+<td><a target="other" href="http://sourceforge.net/projects/flmobf/">flmObf</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Free (BSD)</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.chez.com/vasile/">Vasile Calmatui</a></td>
+<td><a target="other" href="http://www.chez.com/vasile/obfu/VasObfuLite.html">VasObfuLite</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Free</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www-i2.informatik.rwth-aachen.de/~markusj/">Markus Jansen</a></td>
+<td><a target="other" href="http://www-i2.informatik.rwth-aachen.de/~markusj/jopt/">Jopt</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>(disappeared?)</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.primenet.com/~ej">Eron Jokipii</a></td>
+<td><a target="other" href="http://www.primenet.com/~ej">Jobe</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>(disappeared?)</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://jrc.krdl.org.sg/">JRC</a></td>
+<td><a target="other" href="http://jrc.krdl.org.sg/decaf/">DeCaf</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>(disappeared?)</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.plumbdesign.com/">Plumb Design</a></td>
+<td><a target="other" href="http://www.condensity.com/">Condensity</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial (discontinued)</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.4thpass.com/">4th Pass</a></td>
+<td><a target="other" href="http://www.4thpass.com/">SourceGuard</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial (discontinued?)</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.sbktech.org/">HashJava</a></td>
+<td><a target="other" href="http://www.sbktech.org/">HashJava</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial (disappeared?)</td>
+</tr>
+
+</table>
+<p>
+All trademarks are property of their respective holders.
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+
+</body>
+</html>
diff --git a/docs/checkmark.gif b/docs/checkmark.gif
new file mode 100644
index 0000000..8afa677
Binary files /dev/null and b/docs/checkmark.gif differ
diff --git a/docs/downloads.html b/docs/downloads.html
new file mode 100644
index 0000000..c695e33
--- /dev/null
+++ b/docs/downloads.html
@@ -0,0 +1,221 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard Downloads</title>
+</head>
+<body>
+
+<h2>Downloads</h2>
+
+<b>ProGuard</b> is distributed under the GNU General Public License.
+Please refer to the <a href="license.html">license page</a> for more details.
+<p>
+<b>ProGuard</b> is written in Java. It requires a Java 2 runtime environment.
+<p>
+You can download the latest release (containing the program jar, the
+documentation you're reading now, examples, and source code) from this
+location:
+<p>
+<center><a href="http://sourceforge.net/project/showfiles.php?group_id=54750"
+target="other">Download section</a> (at <a
+href="http://sourceforge.net/projects/proguard/"
+target="other">SourceForge</a>)</center>
+<p>
+
+If you're still working with an older version of <b>ProGuard</b>, check out
+the summary of changes below, to see if you're missing something essential.
+Better look at the up-to-date <a
+href="http://proguard.sourceforge.net/downloads.html">on-line version</a> if
+you're reading a local copy of this page. The download section may also contain
+updates with sub-minor version numbers. These versions are typically released
+shortly after their parent versions, for applying emergency fixes.
+<p>
+
+<h3>Version 3.2</h3>
+<ul>
+<li>Fixed JDK5.0 processing bugs.
+<li>Fixed optimization bugs.
+<li>Fixed relative paths in Ant task.
+<li>Improved speed of shrinking step.
+<li>Updated documentation and examples.
+</ul>
+
+<h3>Version 3.1</h3>
+<ul>
+<li>Improved obfuscation and shrinking of private class members.
+<li>Added inlining of interfaces with single implementations.
+<li>Added option to specify obfuscation dictionary.
+<li>Added option to read package visible library class members.
+<li>Extended support for JDK5.0 attributes.
+<li>Fixed various optimization bugs.
+<li>Modified Ant task to accept paths instead of filesets.
+<li>Fixed two Ant task bugs.
+<li>Updated documentation and examples.
+</ul>
+
+<h3>Version 3.0</h3>
+<ul>
+<li>Added bytecode optimization step, inbetween shrinking step and obfuscation
+    step.
+<li>Generalized filtered recursive reading and writing of jars, wars, ears,
+    zips, and directories.
+<li>Added support for grouping input and output jars, wars, ears, zips, and
+    directories.
+<li>Added support for applying mapping files to library classes.
+<li>Removed <code>-resourcejars</code> option. Resources should now be read
+    using regular <code>-injars</code> options, using filters if necessary.
+<li>Rewrote Ant task. Input and output modification dates are not checked at
+    the moment. Minor changes in XML schema:
+    <ul>
+    <li>Filters now specified using attributes.
+    <li>'<code>outjars</code>' now nested element instead of attribute.
+    <li>'<code>type</code>' attribute of <code><method></code> element no
+        longer defaults to '<code>void</code>'.
+    <li><code><</code> and <code>></code> characters now have to be
+        encoded in embedded configurations.
+    <li><code><proguardconfiguration></code> task no longer accepts
+        attributes.
+    </ul>
+<li>Updated J2ME WTK plugin, now customizable through configuration file.
+<li>Updated GUI.
+<li>Fixed various processing bugs.
+<li>Fixed ReTrace parsing bugs.
+<li>Improved jar compression.
+<li>Updated documentation and examples.
+</ul>
+
+<h3>Version 2.1</h3>
+<ul>
+<li>Added support for JDK1.5 classes.
+<li>Added additional wildcard for matching primitive types.
+<li>Added possibility to switch off notes about duplicate class definitions.
+<li>Fixed use of multiple filters on output jars.
+<li>Fixed option to keep all attributes.
+<li>Fixed various Ant task bugs.
+<li>Updated documentation and examples.
+</ul>
+
+<h3>Version 2.0</h3>
+<ul>
+<li>Added a graphical user interface for ProGuard and ReTrace.
+<li>Added <code>-applymapping</code> option for incremental obfuscation.
+<li>Added support for filtering input and output files.
+<li>Added support for the J++ <code>SourceDir</code> attribute.
+<li>Improved detection of <code>.class</code> constructs.
+<li>Improved handling of misplaced manifest files.
+<li>Improved implementation of ReTrace.
+<li>Worked around String UTF-8 encoding bug affecting foreign characters.
+<li>Fixed exception when ignoring warnings.
+<li>Fixed various Ant task bugs.
+<li>Updated documentation and examples.
+</ul>
+
+<h3>Version 1.7</h3>
+<ul>
+<li>Fixed various Ant task bugs.
+<li>Fixed ClassCastException due to explicitly used abstract classes with
+    implicitly used interfaces targeted at JRE1.2 (the default in JDK1.4).
+<li>Fixed <code>-defaultpackage</code> bug for protected classes and class
+    members.
+<li>Fixed ReTrace bug when retracing without line number tables.
+<li>Worked around zip package problems with duplicate out entries and rogue
+    manifest files.
+<li>Added work-around for handling malformed legacy interface class files.
+<li>Updated documentation and examples.
+</ul>
+
+<h3>Version 1.6</h3>
+<ul>
+<li>Added support for Ant.
+<li>Added support for the J2ME Wireless Toolkit.
+<li>Added support for reading and writing directory hierarchies.
+<li>Added option for specifying resource jars and directories.
+<li>Added support for wildcards in class member specifications.
+<li>Improved handling of the <code>-defaultpackage</code> option.
+<li>Improved stack trace parsing in ReTrace tool.
+<li>Fixed processing of libraries containing public as well as non-public
+    extensions of non-public classes.
+<li>Fixed examples for processing libraries, midlets, and serializable code.
+<li>Updated documentation and examples.
+</ul>
+
+<h3>Version 1.5</h3>
+<ul>
+<li>Fixed processing of retrofitted library interfaces.
+<li>Fixed processing of <code>.class</code> constructs in internal classes
+    targeted at JRE1.2 (the default in JDK1.4).
+<li>Fixed <code>-dump</code> option when <code>-outjar</code> option is not
+    present.
+<li>Updated documentation and examples.
+</ul>
+
+<h3>Version 1.4</h3>
+<ul>
+<li>Now copying resource files over from the input jars to the output jar.
+<li>Added option to obfuscate using lower-case class names only.
+<li>Added better option for obfuscating native methods.
+<li>Added option not to ignore non-public library classes.
+<li>Added automatic .class detection for classes compiled with Jikes.
+<li>Updated documentation and examples.
+</ul>
+
+<h3>Version 1.3</h3>
+<ul>
+<li>Added support for wildcards in class names.
+<li>Added tool to de-obfuscate stack traces.
+<li>Added options to print processing information to files.
+<li>Added option to rename source file attributes.
+<li>Fixed processing of implicitly used interfaces targeted at JRE1.2 (the
+    default in JDK1.4)
+<li>Fixed processing of configurations with negated access modifiers.
+<li>Fixed duplicate class entry bug.
+<li>Updated documentation and examples.
+</ul>
+
+<h3>Version 1.2</h3>
+<ul>
+<li>Improved speed.
+<li>Fixed processing of classes targeted at JRE1.2 (the default in JDK1.4)
+    with references to their own subclasses.
+<li>Fixed processing of static initializers in J2ME MIDP applications.
+<li>Fixed processing of retrofitted interfaces (again).
+<li>Added more flexible handling of white space in configuration.
+<li>Updated documentation.
+</ul>
+
+<h3>Version 1.1</h3>
+<ul>
+<li>Added automatic detection of <code>Class.forName("MyClass")</code>,
+    <code>MyClass.class</code>, and
+    <code>(MyClass)Class.forName(variable).newInstance()</code> constructs.
+    This greatly simplifies configuration.
+<li>Added options to keep class names and class member names without affecting
+    any shrinking. They are mostly useful for native methods and serializable
+    classes.
+<li>Fixed processing of retrofitted interfaces, like:
+    <pre>
+    class A     { m() {...} }
+    interface I { m();      }
+    class B extends A implements I
+    </pre>
+<li>Added handling of missing/invalid manifest file in input jar.
+<li>Updated documentation and examples.
+</ul>
+
+<h3>Version 1.0</h3>
+<ul>
+<li>First public release, based on class parsing code from Mark Welsh's
+    <b>RetroGuard</b>.
+</ul>
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+
+</body>
+</html>
diff --git a/docs/drop1.gif b/docs/drop1.gif
new file mode 100644
index 0000000..426d856
Binary files /dev/null and b/docs/drop1.gif differ
diff --git a/docs/drop2.gif b/docs/drop2.gif
new file mode 100644
index 0000000..b607542
Binary files /dev/null and b/docs/drop2.gif differ
diff --git a/docs/drop3.gif b/docs/drop3.gif
new file mode 100644
index 0000000..11ce424
Binary files /dev/null and b/docs/drop3.gif differ
diff --git a/docs/feedback.html b/docs/feedback.html
new file mode 100644
index 0000000..43ae9e9
--- /dev/null
+++ b/docs/feedback.html
@@ -0,0 +1,106 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-script-type" content="text/javascript">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard Feedback</title>
+</head>
+<body>
+
+<h2>Feedback</h2>
+
+By now, I've invested a fair amount of time in <b>ProGuard</b>. You can help
+by providing feedback! If you have good experiences, bad experiences,
+problems, bugs, bug fixes, ideas, encouragements, etc. to share, please go
+ahead:
+<p>
+<ul>
+<li>A good place to share your thoughts about <b>ProGuard</b> is the <a
+    href="http://sourceforge.net/forum/forum.php?forum_id=182455"
+    target="other">open discussion forum</a> (at <a
+    href="http://sourceforge.net/projects/proguard/"
+    target="other">SourceForge</a>).
+    <p>
+
+<li>If you need help, please go to the <a
+    href="http://sourceforge.net/forum/forum.php?forum_id=182456"
+    target="other">help forum</a> (at <a
+    href="http://sourceforge.net/projects/proguard/"
+    target="other">SourceForge</a>).
+    <p>
+
+<li>You can submit and consult bug reports on the <a
+
+    href="http://sourceforge.net/tracker/?atid=474704&group_id=54750&func=browse"
+    target="other">bug tracking page</a> (at <a
+    href="http://sourceforge.net/projects/proguard/"
+    target="other">SourceForge</a>).
+    <p>
+
+<li>Likewise, you can submit and consult feature requests on the <a
+
+    href="http://sourceforge.net/tracker/?atid=474707&group_id=54750&func=browse"
+    target="other">feature request page</a> (at <a
+    href="http://sourceforge.net/projects/proguard/"
+    target="other">SourceForge</a>).
+    <p>
+
+<li>If you just want to stay abreast of the latest developments, you can
+    subscribe to the announcements of new releases in the <a
+    href="http://sourceforge.net/project/showfiles.php?group_id=54750"
+    target="other">download section</a> at <a
+    href="http://sourceforge.net/projects/proguard/"
+    target="other">SourceForge</a>, or the
+    <a href="http://software.freshmeat.net/projects/proguard/"
+    target="other">project page</a> at <a
+    href="http://software.freshmeat.net/">FreshMeat</a>.
+    <p>
+
+<li>If you have detailed bug reports with attachments, or just some
+    encouragements, or if <b>ProGuard</b> is useful to you and you want to
+    support it with software or hardware, you can mail me directly at
+
+<script type="text/javascript" language="JavaScript">
+<!--
+document.write("<a href=\"ma");
+document.write("ilto:");
+document.write("lafortune");
+document.write("@");
+document.write("users.sourceforge.net\">");
+document.write("lafortune");
+document.write("@");
+document.write("users.sourceforge.net");
+document.write("</a>");
+document.write(" <em>or</em> at ");
+document.write("<a href=\"ma");
+document.write("ilto:");
+document.write("eric");
+document.write("@");
+document.write("graphics.cornell.edu\">");
+document.write("eric");
+document.write("@");
+document.write("graphics.cornell.edu");
+document.write("</a>");
+//-->
+</script>
+<noscript>
+< lafortune @ users . sourceforge . net >
+<em>or</em> at
+< eric @ graphics . cornell . edu > (please remove the spaces)
+</noscript>
+.
+</ul>
+<p>
+I can't promise a swift answer, or any answer at all, for that matter, but I'd
+love to see your responses.
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+
+</body>
+</html>
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000..802be28
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,54 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-script-type" content="text/javascript">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard</title>
+</head>
+<frameset
+ rows="50,*"
+ framespacing="0"
+ frameborder="no">
+
+<frame
+ name="title"
+ src="title.html"
+ scrolling="no"
+ marginwidth="0"
+ marginheight="0"
+ noresize>
+
+<frameset
+ cols="120,*"
+ framespacing="0"
+ frameborder="no">
+
+<frame
+ name="sections"
+ src="sections.html"
+ scrolling="no"
+ marginwidth="0"
+ marginheight="0"
+ noresize>
+
+<frame
+ name="main"
+ src="main.html"
+ scrolling="auto"
+ marginwidth="10"
+ marginheight="10"
+ noresize>
+
+</frameset>
+</frameset>
+
+<noframes>
+<body>
+Your browser doesn't support frames, but that's cool.
+<p>
+You can go straight to the <a href="main.html">main page</a>.
+</body>
+</noframes>
+</html>
diff --git a/docs/license.html b/docs/license.html
new file mode 100644
index 0000000..97d1f78
--- /dev/null
+++ b/docs/license.html
@@ -0,0 +1,46 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard License</title>
+</head>
+<body>
+
+<h2>License</h2>
+
+<b>ProGuard</b> is free. I wrote it for the challenge of it. And for eternal
+fame and glory, of course. You can use it for whatever purpose you deem
+useful. This includes processing your applications, commercial or not. Your
+code obviously remains yours after processing.
+<p>
+
+<b>ProGuard</b> is copyrighted, but the distribution license provides you with
+some rights for modifying and redistributing its code and its documentation.
+<p>
+<b>ProGuard</b> is distributed under the term of the <a href="GPL.html">GNU
+General Public License</a> (GPL), as published by the <a
+href="http://www.fsf.org/" target="other">Free Software Foundation</a> (FSF).
+In short, this means that you may freely redistribute the program, modified or
+as is, on the condition that you make the complete source code available as
+well. If you develop a program that is linked with <b>ProGuard</b>, the
+program as a whole has to be distributed at no charge under the GPL. I am
+granting a <a href="GPL_exception.html">special exception</a> to the latter
+clause (in wording suggested by the <a
+href="http://www.gnu.org/copyleft/gpl-faq.html#GPLIncompatibleLibs"
+target="other">FSF</a>), for combinations with Apache Ant, the Sun J2ME
+Wireless Toolkit, and the Eclipse Platform.
+
+<p>
+The user documentation represents an important part of this work. It may only
+be redistributed without changes, along with the unmodified version of the
+code.
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
diff --git a/docs/luciadlogo.png b/docs/luciadlogo.png
new file mode 100644
index 0000000..0147ce3
Binary files /dev/null and b/docs/luciadlogo.png differ
diff --git a/docs/main.html b/docs/main.html
new file mode 100644
index 0000000..b782bf1
--- /dev/null
+++ b/docs/main.html
@@ -0,0 +1,79 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard Main</title>
+</head>
+<body>
+
+<h2>Main</h2>
+
+<p class="intro">
+<b>ProGuard</b> is a free Java class file shrinker, optimizer, and obfuscator.
+It can detect and remove unused classes, fields, methods, and attributes. It
+can then optimize bytecode and remove unused instructions. Finally, it can
+rename the remaining classes, fields, and methods using short meaningless
+names. The resulting jars are smaller and harder to reverse-engineer.
+</p>
+<p>
+More compact jar files also means smaller storage requirements, faster
+transfer of applications across networks, faster loading, and smaller memory
+footprints.
+<p>
+<b>ProGuard</b>'s main advantage compared to other Java obfuscators is
+probably its compact template-based configuration. A few intuitive command
+line options or a simple configuration file are usually sufficient. For
+instance, the following configuration option preserves all applets in a jar:
+<pre>
+    -keep public class * extends java.applet.Applet
+</pre>
+The user manual explains all available options and shows more examples of this
+powerful configuration style.
+<p>
+<b>ProGuard</b> is fast. It only takes seconds to process programs and
+libraries of several megabytes. The results section presents actual figures
+for a number of applications.
+<p>
+<b>ProGuard</b> is a command-line tool with an optional graphical user
+interface. It also comes with plugins for Ant and for the J2ME Wireless
+Toolkit.
+<p>
+<p class="intro">
+Version 3.0 introduced bytecode optimization, reading and writing of nested
+jars, and a brand new Ant task. Please report any problems, so they can be
+fixed soon.
+</p>
+The following sections provide more detailed information:
+<ul>
+<li><a href="main.html">Main</a>: this overview page.
+<li><a href="results.html">Results</a>: some results obtained with
+    <b>ProGuard</b>, including timings and memory usage.
+<li><a href="FAQ.html">FAQ</a>: answers to some Frequently Asked Questions.
+<li><a href="manual/index.html">Manual</a>: the complete <b>ProGuard</b> user
+    manual, with examples and troubleshooting tips.
+<li><a href="quality.html">Quality</a>: a discussion of the (excellent) quality
+    of <b>ProGuard</b>'s code.
+<li><a href="screenshots.html">Screenshots</a>: some impressions of what            <b>ProGuard</b> looks like.
+<li><a href="testimonials.html">Testimonials</a>: what users think of
+    <b>ProGuard</b>.
+<li><a href="license.html">License</a>: <b>ProGuard</b> is free, under a GPL
+    license.
+<li><a href="downloads.html">Downloads</a>: download the <b>ProGuard</b>
+    package yourself.
+<li><a href="feedback.html">Feedback</a>: tell me about your experiences, or
+    learn from others on our forums.
+<li><a href="acknowledgements.html">Acknowledgements</a>: people who have been
+    helpful.
+<li><a href="alternatives.html">Alternatives</a>: other Java obfuscators and
+    shrinking programs.
+</ul>
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
diff --git a/docs/manual/ant.html b/docs/manual/ant.html
new file mode 100644
index 0000000..1043397
--- /dev/null
+++ b/docs/manual/ant.html
@@ -0,0 +1,417 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>Ant Task</title>
+</head>
+<body>
+
+<h2>Ant Task</h2>
+
+<b>ProGuard</b> can be run as a task in the Java-based build tool Ant (version
+1.6.0 or higher).
+<p>
+
+Before you can use the <code>proguard</code> task, you have to tell Ant about
+this new task. The easiest way is to add the following line to your
+<code>build.xml</code> file:
+<p>
+
+<pre>
+<taskdef resource="proguard/ant/task.properties"
+         classpath="/usr/local/java/proguard/lib/proguard.jar" />
+</pre>
+<p>
+
+Please make sure the class path is set correctly for your system.
+<p>
+
+There are three ways to configure the ProGuard task: using an external
+configuration file, using embedded ProGuard configuration options, and using
+the equivalent XML configuration tags. These three ways can be combined,
+depending on practical circumstances and personal preference.
+<p>
+
+<h3>1. An external ProGuard configuration file</h3>
+
+The simplest way to use the ProGuard task in an Ant build file is to keep
+your ProGuard configuration file, and include it from Ant. You can include
+your ProGuard configuration file by setting the <a
+href="#configuration"><code>configuration</code></a> attribute of your
+<code>proguard</code> task. Your ant build file will then look like this:
+<p>
+
+<pre>
+<taskdef resource="proguard/ant/task.properties"
+         classpath="/usr/local/java/proguard/lib/proguard.jar" />
+<proguard configuration="myconfigfile.pro"/>
+</pre>
+<p>
+
+This is a convenient option if you prefer ProGuard's configuration style over
+XML, if you want to keep your build file small, or if you have to share your
+configuration with developers who don't use Ant.
+<p>
+
+<h3>2. Embedded ProGuard configuration options</h3>
+
+Instead of keeping an external ProGuard configuration file, you can also copy
+the contents of the file into the nested text of the <code>proguard</code> task
+(the PCDATA area). Your Ant build file will then look like this:
+<p>
+
+<pre>
+<taskdef resource="proguard/ant/task.properties"
+         classpath="/usr/local/java/proguard/lib/proguard.jar" />
+<proguard>
+  -libraryjars ${java.home}/lib/rt.jar
+  -injars      in.jar
+  -outjars     out.jar
+
+  -keepclasseswithmembers public class * {
+      public static void main(java.lang.String[]);
+  }
+</proguard>
+</pre>
+<p>
+
+Some minor syntactical changes are required in order to conform with the XML
+standard.
+<p>
+
+Firstly, the <code>#</code> chararacter cannot be used for comments in an XML
+file. Comments must be enclosed by an opening <code><!--</code> and a
+closing <code>--></code>. All occurrences of the <code>#</code> chararacter
+can be removed.
+<p>
+
+Secondly, the use of <code><</code> and <code>></code> characters would
+upset the structure of the XML build file. Environment variables are now
+enclosed by an opening <code>${</code> and a closing <code>}</code>. This
+syntax also allows you to use Ant properties within the ProGuard
+configuration. Other occurrences of <code><</code> and <code>></code>
+have to be encoded as <code>&lt;</code> and <code>&gt;</code>.
+<p>
+
+<h3>3. XML configuration tags</h3>
+
+If you really prefer a full-blown XML configuration, you can replace the
+ProGuard configuration options by the equivalent XML configuration tags. The
+remainder of this page presents the supported tags. For a more extensive
+discussion of their meaning, please refer to the traditional <a
+href="usage.html">Usage</a> section.
+<p>
+
+<a name="proguard"> </a>
+<h2>Task Attributes and Nested Elements</h2>
+
+The <code><b><proguard></b></code> task and the
+<code><b><proguardconfiguration></b></code> task can have the following
+attributes (only for <code><proguard></code>) and nested
+elements:
+
+<dl>
+
+<dt><a name="configuration"><code><b>configuration</b></code></a> =
+    "<i>filename</i>"</dt>
+<dd>Read and merge options from the given ProGuard-style configuration
+    file.</dd>
+
+<dt><code><b>skipnonpubliclibraryclasses</b></code> = "<i>boolean</i>"
+    (default = true)</dt>
+<dd>Ignore non-public library classes.</dd>
+
+<dt><code><b>skipnonpubliclibraryclassmembers</b></code> = "<i>boolean</i>"
+    (default = true)</dt>
+<dd>Ignore package visible library class members.</dd>
+
+<dt><code><b>printseeds</b></code> = "<i>boolean or filename</i>"
+    (default = false)</dt>
+<dd>List classes and class members matched by the various <code>keep</code>
+    commands, to the standard output or to the given file.</dd>
+
+<dt><code><b>shrink</b></code> = "<i>boolean</i>" (default = true)</dt>
+<dd>Shrink the input class files.</dd>
+
+<dt><code><b>printusage</b></code> = "<i>boolean or filename</i>"
+    (default = false)</dt>
+<dd>List dead code of the input class files, to the standard output or to the
+    given file.</dd>
+
+<dt><code><b>optimize</b></code> = "<i>boolean</i>" (default = true)</dt>
+<dd>Optimize the input class files.</dd>
+
+<dt><code><b>allowaccessmodification</b></code> = "<i>boolean</i>"
+    (default = false)</dt>
+<dd>Allow the access modifiers of classes and class members to be modified,
+    while optimizing.</dd>
+
+<dt><code><b>obfuscate</b></code> = "<i>boolean</i>" (default = true)</dt>
+<dd>Obfuscate the input class files.</dd>
+
+<dt><code><b>printmapping</b></code> = "<i>boolean or filename</i>"
+    (default = false)</dt>
+<dd>Print the mapping from old names to new names for classes and class members
+    that have been renamed, to the standard output or to the given file.</dd>
+
+<dt><code><b>applymapping</b></code> = "<i>filename</i>"</dt>
+<dd>Reuse the given mapping, for incremental obfuscation.</dd>
+
+<dt><code><b>obfuscationdictionary</b></code> = "<i>filename</i>"</dt>
+<dd>Use the words in the given text file as obfuscated method names.</dd>
+
+<dt><code><b>overloadaggressively</b></code> = "<i>boolean</i>"
+    (default = false)</dt>
+<dd>Apply aggressive overloading while obfuscating.</dd>
+
+<dt><code><b>defaultpackage</b></code> = "<i>package_name</i>"</dt>
+<dd>Repackage all class files that are renamed into the single given
+    package.</dd>
+
+<dt><code><b>usemixedcaseclassnames</b></code> = "<i>boolean</i>"
+    (default = true)</dt>
+<dd>Generate mixed-case class names while obfuscating.</dd>
+
+<dt><code><b>renamesourcefileattribute</b></code> = "<i>string</i>"</dt>
+<dd>Put the given constant string in the <code>SourceFile</code>
+    attributes. The default is to keep the original string.</dd>
+
+<dt><code><b>verbose</b></code> = "<i>boolean</i>"
+    (default = false)</dt>
+<dd>Write out some more information during processing.</dd>
+
+<dt><code><b>note</b></code> = "<i>boolean</i>" (default = true)</dt>
+<dd>Print notes about class casts of variable dynamically created objects.</dd>
+
+<dt><code><b>warn</b></code> = "<i>boolean</i>" (default = true)</dt>
+<dd>Warn about unresolved references at all.</dd>
+
+<dt><code><b>ignorewarnings</b></code> = "<i>boolean</i>"
+    (default = false)</dt>
+<dd>Print warnings about unresolved references, but continue processing
+    anyhow. The default is not to ignore warnings. <i>Only use this option if
+    you know what you're doing!</i></dd>
+
+<dt><code><b>dump</b></code> = "<i>boolean or filename</i>"
+    (default = false)</dt>
+<dd>Write out the internal structure of the processed class files, to the
+    standard output or to the given file.</dd>
+
+<dt><code><b><injar</b></code>
+    <a href="#classpath"><i>class_path</i></a>
+    <code><b>/></b></code></dt>
+<dd>Specifies the program jars (or wars, ears, zips, or directories).</dd>
+
+<dt><code><b><outjar</b></code>
+    <a href="#classpath"><i>class_path</i></a>
+    <code><b>/></b></code></dt>
+<dd>Specifies the name of the output jars (or wars, ears, zips, or
+    directories).</dd>
+
+<dt><code><b><libraryjar</b></code>
+    <a href="#classpath"><i>class_path</i></a>
+    <code><b>/></b></code></dt>
+<dd>Specifies the library jars (or wars, ears, zips, or directories).</dd>
+
+<dt><code><b><keep</b></code>
+    <a href="#classspecification"><i>class_specification</i></a>
+    <code><b>></b></code>
+    <a href="#classmemberspecification"><i>class_member_specifications</i></a>
+    <code><b></keep></b></code></dt>
+<dd>Preserve the specified classes <i>and</i> class members.</dd>
+
+<dt><code><b><keepclassmembers</b></code>
+    <a href="#classspecification"><i>class_specification</i></a>
+    <code><b>></b></code>
+    <a href="#classmemberspecification"><i>class_member_specifications</i></a>
+    <code><b></keepclassmembers></b></code></dt>
+<dd>Preserve the specified class members, if their classes are preserved as
+    well.</dd>
+
+<dt><code><b><keepclasseswithmembers</b></code>
+    <a href="#classspecification"><i>class_specification</i></a>
+    <code><b>></b></code>
+    <a href="#classmemberspecification"><i>class_member_specifications</i></a>
+    <code><b></keepclasseswithmembers></b></code></dt>
+<dd>Preserve the specified classes <i>and</i> class members, if all of the
+    specified class members are present.</dd>
+
+<dt><code><b><keepnames</b></code>
+    <a href="#classspecification"><i>class_specification</i></a>
+    <code><b>></b></code>
+    <a href="#classmemberspecification"><i>class_member_specifications</i></a>
+    <code><b></keepnames></b></code></dt>
+<dd>Preserve the names of the specified classes <i>and</i> class members (if
+    they aren't removed in the shrinking step).</dd>
+
+<dt><code><b><keepclassmembernames</b></code>
+    <a href="#classspecification"><i>class_specification</i></a>
+    <code><b>></b></code>
+    <a href="#classmemberspecification"><i>class_member_specifications</i></a>
+    <code><b></keepclassmembernames></b></code></dt>
+<dd>Preserve the names of the specified class members (if they aren't removed
+    in the shrinking step).</dd>
+
+<dt><code><b><keepclasseswithmembernames</b></code>
+    <a href="#classspecification"><i>class_specification</i></a>
+    <code><b>></b></code>
+    <a href="#classmemberspecification"><i>class_member_specifications</i></a>
+    <code><b></keepclasseswithmembernames></b></code></dt>
+<dd>Preserve the names of the specified classes <i>and</i> class members, if
+    all of the specified class members are present (after the shrinking
+    step).</dd>
+
+<dt><code><b><assumenosideeffects</b></code>
+    <a href="#classspecification"><i>class_specification</i></a>
+    <code><b>></b></code>
+    <a href="#classmemberspecification"><i>class_member_specifications</i></a>
+    <code><b></assumenosideeffects></b></code></dt>
+<dd>Assume that the specified methods don't have any side effects, while
+    optimizing.</dd>
+
+<dt><code><b><keepattribute name = </b></code>"<i>attribute_name</i>"
+    <code><b>/></b></code></dt>
+<dd>Preserve the given optional Java bytecode attribute, with optional
+    wildcards. If no name is given, all attributes are preserved.</dd>
+
+<dt><code><b><configuration refid = </b></code>"<i>ref_id</i>"
+    <code><b>/></b></code></dt>
+<dd>Includes the configuration specified in the
+    <code><proguardconfiguration></code> task (or
+    <code><proguard></code> task) with the attribute <code>id</code> =
+    "<i>ref_id</i>". Note that only the nested elements of this configuration
+    are considered, not the attributes.</dd>
+
+</dl>
+
+<a name="classpath"> </a>
+<h2>Class Path Attributes and Nested Elements</h2>
+
+The jar tags are path tags, so they can have any of the path attributes (or
+nested elements). The most common attributes are:
+
+<dl>
+
+<dt><code><b>path</b></code> = "<i>path</i>"</dt>
+<dd>The names of the jars (or wars, ears, zips, or directories), separated by
+    the path separator.</dd>
+
+<dt><code><b>location</b></code> = "<i>name</i>" (or <code><b>file</b></code>
+    = "<i>name</i>", or <code><b>dir</b></code> = "<i>name</i>", or
+    <code><b>name</b></code> = "<i>name</i>")</dt>
+<dd>Alternatively, the name of a single jar (or war, ear, zip, or
+    directory).</dd>
+
+<dt><code><b>refid</b></code> = "<i>ref_id</i>"</dt>
+<dd>Alternatively, a reference to the path or file set with the attribute
+    <code>id</code> = "<i>ref_id</i>".</dd>
+
+</dl>
+
+In addition, the jar tags can have ProGuard-style filter attributes (as
+described in the section on <a href="usage.html#filters">Filters</a>):
+
+<dl>
+
+<dt><code><b>filter</b></code> = "<i>filter</i>"</dt>
+<dd>An optional filter for all class file names and resource file names that
+    are encountered.</dd>
+
+<dt><code><b>jarfilter</b></code> = "<i>jar_filter</i>"</dt>
+<dd>An optional filter for all jar names that are encountered.</dd>
+
+<dt><code><b>warfilter</b></code> = "<i>war_filter</i>"</dt>
+<dd>An optional filter for all war names that are encountered.</dd>
+
+<dt><code><b>earfilter</b></code> = "<i>ear_filter</i>"</dt>
+<dd>An optional filter for all ear names that are encountered.</dd>
+
+<dt><code><b>zipfilter</b></code> = "<i>zip_filter</i>"</dt>
+<dd>An optional filter for all zip names that are encountered.</dd>
+
+</dl>
+
+<a name="classspecification"> </a>
+<h2>Class Specification Attributes and Nested Elements</h2>
+
+The keep tags can have the following <i>class_specification</i> attributes and
+<i>class_member_specifications</i> nested elements:
+
+<dl>
+
+<dt><code><b>access</b></code> = "<i>access_modifiers</i>"</dt>
+<dd>The optional access modifiers of the class. Any space-separated list of
+    "public", "final", and "abstract", with optional negators "!".</dd>
+
+<dt><code><b>type</b></code> = "<i>type</i>"</dt>
+<dd>The optional type of the class: one of "class", "interface", or
+    "!interface".</dd>
+
+<dt><code><b>name</b></code> = "<i>class_name</i>"</dt>
+<dd>The optional fully qualified name of the class, with optional
+    wildcards.</dd>
+
+<dt><code><b>extends</b></code> = "<i>class_name</i>"</dt>
+<dd>The optional fully qualified name of the class the specified classes
+    must extend, with optional wildcards.</dd>
+
+<dt><code><b>implements</b></code> = "<i>class_name</i>"</dt>
+<dd>The optional fully qualified name of the class the specified classes
+    must implement, with optional wildcards.</dd>
+
+<dt><code><b><field</b></code>
+    <a href="#classmemberspecification"><i>class_member_specification</i></a>
+    <code><b>/></b></code></dt>
+<dd>Specifies a field.</dd>
+
+<dt><code><b><method</b></code>
+    <a href="#classmemberspecification"><i>class_member_specification</i></a>
+    <code><b>/></b></code></dt>
+<dd>Specifies a method.</dd>
+
+<dt><code><b><constructor</b></code>
+    <a href="#classmemberspecification"><i>class_member_specification</i></a>
+    <code><b>/></b></code></dt>
+<dd>Specifies a constructor.</dd>
+
+</dl>
+
+<a name="classmemberspecification"> </a>
+<h2>Class Member Specification Attributes</h2>
+
+The class member tags can have the following <i>class_member_specification</i>
+attributes:
+
+<dl>
+
+<dt><code><b>access</b></code> = "<i>access_modifiers</i>"</dt>
+<dd>The optional access modifiers of the class. Any space-separated list of
+    "public", "protected", "private", "static", etc., with optional negators
+    "!".</dd>
+
+<dt><code><b>type</b></code> = "<i>type</i>"</dt>
+<dd>The optional fully qualified type of the class member, with optional
+    wildcards. Not applicable for constructors, but required for methods for
+    which the <code>parameters</code> attribute is specified.</dd>
+
+<dt><code><b>name</b></code> = "<i>name</i>"</dt>
+<dd>The optional name of the class member, with optional wildcards. Not
+    applicable for constructors.</dd>
+
+<dt><code><b>parameters</b></code> = "<i>parameters</i>"</dt>
+<dd>The optional comma-separated list of fully qualified method parameters,
+    with optional wildcards. Not applicable for fields, but required for
+    constructors, and for methods for which the <code>type</code> attribute is
+    specified.</dd>
+
+</dl>
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
diff --git a/docs/manual/examples.html b/docs/manual/examples.html
new file mode 100644
index 0000000..647ca03
--- /dev/null
+++ b/docs/manual/examples.html
@@ -0,0 +1,684 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard Examples</title>
+</head>
+<body>
+
+<h2>Examples</h2>
+
+Some typical useful configurations:
+<ol>
+<li><a href="#application">A typical application</a>
+<li><a href="#applet">A typical applet</a>
+<li><a href="#midlet">A typical midlet</a>
+<li><a href="#applications">All possible applications in the input jars</a>
+<li><a href="#applets">All possible applets in the input jars</a>
+<li><a href="#midlets">All possible midlets in the input jars</a>
+<li><a href="#servlets">All possible servlets in the input jars</a>
+<li><a href="#library">Processing a library</a>
+<li><a href="#native">Processing native methods</a>
+<li><a href="#serializable">Processing serializable classes</a>
+<li><a href="#beans">Processing bean classes</a>
+<li><a href="#enumerations">Processing enumeration classes</a>
+<li><a href="#annotations">Processing annotations</a>
+<li><a href="#rmi">Processing RMI code</a>
+<li><a href="#stacktrace">Producing useful obfuscated stack traces</a>
+<li><a href="#multiple">Processing multiple applications at once</a>
+<li><a href="#incremental">Incremental obfuscation</a>
+<li><a href="#deadcode">Finding dead code</a>
+<li><a href="#structure">Printing out the internal structure of class files</a>
+</ol>
+
+<a name="application"> </a>
+<h3>A typical application</h3>
+To shrink, optimize, and obfuscate the ProGuard application itself, one would
+typically create a configuration file <code>proguard.pro</code> and then type:
+<pre>
+java -jar proguard.jar @proguard.pro
+</pre>
+<p>
+
+The configuration file would contain the following options:
+<pre>
+-injars       proguard.jar
+-outjars      proguard_out.jar
+-libraryjars  <java.home>/lib/rt.jar
+-printmapping proguard.map
+-overloadaggressively
+-defaultpackage ''
+-allowaccessmodification
+
+-keep public class proguard.ProGuard {
+    public static void main(java.lang.String[]);
+}
+</pre>
+<p>
+Note the use of the <code><java.home></code> system property; it is
+replaced automatically.
+<p>
+Also note that the type names are fully specified:
+<code>proguard.ProGuard</code> and <code>java.lang.String[]</code>.
+<p>
+The access modifiers <code>public</code> and <code>static</code> are not
+really required in this case, since we know a priori that the specified class
+and method have the proper access flags. It just looks more familiar this way.
+<p>
+We're using the <code>-overloadaggressively</code>,
+<code>-defaultpackage</code>, and <code>-allowaccessmodification</code>
+options to shave off some extra bytes, but we could leave them out as well.
+The <code>-defaultpackage</code> directive moves all classes to the given
+package, in this case the root package. Only <code>proguard.ProGuard</code>
+keeps its original name.
+<p>
+We're writing out an obfuscation mapping file with <code>-printmapping</code>,
+for de-obfuscating any stack traces later on, or for incremental obfuscation of
+extensions.
+<p>
+In general, you might need a few additional directives for processing <a
+href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
+<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
+classes</a>. For processing 'simple' applications like ProGuard, that is not
+required.
+
+<a name="applet"> </a>
+<h3>A typical applet</h3>
+These options shrink, optimize, and obfuscate the applet
+<code>mypackage.MyApplet</code>:
+<pre>
+-injars      in.jar
+-outjars     out.jar
+-libraryjars <java.home>/lib/rt.jar
+
+-keep public class mypackage.MyApplet
+</pre>
+<p>
+The typical applet methods will be preserved automatically, since
+<code>mypackage.MyApplet</code> is an extension of the <code>Applet</code>
+class in the library <code>rt.jar</code>.
+<p>
+If applicable, you should add directives for processing <a
+href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
+<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
+classes</a>.
+
+<a name="midlet"> </a>
+<h3>A typical midlet</h3>
+These options shrink, optimize, and obfuscate the J2ME midlet
+<code>mypackage.MyMIDlet</code>:
+<pre>
+-injars      in.jar
+-outjars     out.jar
+-libraryjars /usr/local/java/wtk2.1/lib/midpapi20.jar
+-libraryjars /usr/local/java/wtk2.1/lib/cldcapi11.jar
+-overloadaggressively
+-defaultpackage ''
+-allowaccessmodification
+
+-keep public class mypackage.MyMIDlet
+</pre>
+<p>
+Note how we're now targeting the J2ME run-time environment of
+<code>midpapi20.jar</code> and <code>cldcapi11.jar</code>, instead of the J2SE
+run-time environment <code>rt.jar</code>. You can target other J2ME
+environments by picking the appropriate jars.
+<p>
+The typical midlet methods will be preserved automatically, since
+<code>mypackage.MyMIDlet</code> is an extension of the <code>MIDlet</code>
+class in the library <code>midpapi20.jar</code>.
+<p>
+If applicable, you should add directives for processing <a
+href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
+<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
+classes</a>.
+<p>
+You must preverify your midlets <i>after</i> having processed them, using
+J2ME's <code>preverify</code> tool. Because this tool unpacks your processed
+jars, you should use ProGuard's <code>-dontusemixedcaseclassnames</code>
+option on platforms with case-insensitive filing systems, such as Windows.
+<p>
+Please note the in-depth article <a
+href="http://developers.sun.com/techtopics/mobility/midp/ttips/proguard/">"Obfuscating
+MIDlet Suites with ProGuard"</a> at <a
+href="http://developers.sun.com/">developers.sun.com</a>.
+
+<a name="applications"> </a>
+<h3>All possible applications in the input jars</h3>
+These options shrink, optimize, and obfuscate all public applications in
+<code>in.jar</code>:
+<pre>
+-injars      in.jar
+-outjars     out.jar
+-libraryjars <java.home>/lib/rt.jar
+-printseeds
+
+-keepclasseswithmembers public class * {
+    public static void main(java.lang.String[]);
+}
+</pre>
+<p>
+Note the use of <code>-keepclasseswithmembers</code>. We don't want to preserve
+all classes, just all classes that have main methods, and those methods.
+<p>
+The <code>-printseeds</code> option prints out which classes exactly will
+be preserved, so we know for sure we're getting what we want.
+<p>
+If applicable, you should add directives for processing <a
+href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
+<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
+classes</a>.
+
+<a name="applets"> </a>
+<h3>All possible applets in the input jars</h3>
+These options shrink, optimize, and obfuscate all public applets in
+<code>in.jar</code>:
+<pre>
+-injars      in.jar
+-outjars     out.jar
+-libraryjars <java.home>/lib/rt.jar
+-printseeds
+
+-keep public class * extends java.applet.Applet
+</pre>
+<p>
+We're simply keeping all classes that extend the <code>Applet</code> class.
+<p>
+Again, the <code>-printseeds</code> option prints out which applets exactly
+will be preserved.
+<p>
+If applicable, you should add directives for processing <a
+href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
+<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
+classes</a>.
+
+<a name="midlets"> </a>
+<h3>All possible midlets in the input jars</h3>
+These options shrink, optimize, and obfuscate all public J2ME midlets in
+<code>in.jar</code>:
+<pre>
+-injars      in.jar
+-outjars     out.jar
+-libraryjars /usr/local/java/wtk2.1/lib/midpapi20.jar
+-libraryjars /usr/local/java/wtk2.1/lib/cldcapi11.jar
+-overloadaggressively
+-defaultpackage ''
+-allowaccessmodification
+-printseeds
+
+-keep public class * extends javax.microedition.midlet.MIDlet
+</pre>
+<p>
+We're simply keeping all classes that extend the <code>MIDlet</code> class.
+<p>
+And again, the <code>-printseeds</code> option prints out which midlets exactly
+will be preserved.
+<p>
+If applicable, you should add directives for processing <a
+href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
+<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
+classes</a>.
+<p>
+You must preverify your midlets <i>after</i> having processed them, using
+J2ME's <code>preverify</code> tool. Because this tool unpacks your processed
+jars, you should use ProGuard's <code>-dontusemixedcaseclassnames</code>
+option on platforms with case-insensitive filing systems, such as Windows.
+
+<a name="servlets"> </a>
+<h3>All possible servlets in the input jars</h3>
+These options shrink, optimize, and obfuscate all public servlets in
+<code>in.jar</code>:
+<pre>
+-injars      in.jar
+-outjars     out.jar
+-libraryjars <java.home>/lib/rt.jar
+-libraryjars /usr/local/java/servlet/servlet.jar
+-printseeds
+
+-keep public class * implements javax.servlet.Servlet
+</pre>
+<p>
+Keeping all servlets is very similar to keeping all applets. The servlet API
+is not part of the standard run-time jar, so we're specifying it as a library.
+Don't forget to use the right path name.
+<p>
+We're then keeping all classes that implement the <code>Servlet</code>
+interface. We're using the <code>implements</code> keyword because it looks
+more familiar in this context, but it is equivalent to <code>extends</code>,
+as far as ProGuard is concerned.
+<p>
+And again, the <code>-printseeds</code> option prints out which servlets
+exactly will be preserved.
+<p>
+If applicable, you should add directives for processing <a
+href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
+<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
+classes</a>.
+
+<a name="library"> </a>
+<h3>Processing a library</h3>
+These options shrink, optimize, and obfuscate an entire library, keeping all
+public and protected classes and class members, native method names, and
+serialization code:
+<pre>
+-injars       in.jar
+-outjars      out.jar
+-libraryjars  <java.home>/lib/rt.jar
+-printmapping out.map
+
+-renamesourcefileattribute SourceFile
+-keepattributes InnerClasses,SourceFile,LineNumberTable,Deprecated,
+                Signature,*Annotation*,EnclosingMethod
+
+-keep public class * {
+    public protected *;
+}
+
+-keepclassmembernames class * {
+    java.lang.Class class$(java.lang.String);
+    java.lang.Class class$(java.lang.String, boolean);
+}
+
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+-keepclassmembers class * extends java.lang.Enum {
+    public **[] values();
+}
+
+-keepclassmembers class * implements java.io.Serializable {
+    static final long serialVersionUID;
+    private void writeObject(java.io.ObjectOutputStream);
+    private void readObject(java.io.ObjectInputStream);
+    java.lang.Object writeReplace();
+    java.lang.Object readResolve();
+}
+</pre>
+<p>
+This configuration should preserve everything we'll ever want to access in the
+library. Only if there are any other non-public classes or methods that are
+invoked dynamically, they should be specified using additional
+<code>-keep</code> directives.
+<p>
+The <code>-keepclassmembernames</code> directive for the <code>class$</code>
+methods is not strictly necessary. These methods are inserted by the
+<code>javac</code> compiler and the <code>jikes</code> compiler respectively,
+to implement the <code>.class</code> construct. ProGuard will automatically
+detect them and deal with them, even when their names have been obfuscated.
+However, older versions of ProGuard and other obfuscators may rely on the
+original method names. It may therefore be helpful to preserve them, in case
+these other obfuscators are ever used for further obfuscation of the library.
+<p>
+We've added some directives for keeping the "Deprecated" attribute, for <a
+href="#stacktrace">useful stack traces</a>, for <a href="#native">native
+methods</a>, for <a href="#enumerations">enumerations</a>, for <a
+href="#serializable">serializable classes</a>, and for <a
+href="#annotations">annotations</a>, which are all discussed in their
+respective examples.
+<p>
+The "InnerClasses" attribute (or more precisely, its source name part) has to
+be preserved as well, for any inner classes that can be referenced from
+outside the library. The <code>javac</code> compiler would be unable to find
+the inner classes otherwise.
+<p>
+The "Signature" attribute is required to be able to access generic types when
+compiling in JDK 5.0.
+
+<a name="native"> </a>
+<h3>Processing native methods</h3>
+If your application, applet, servlet, library, etc., contains native methods,
+you'll want to preserve their names and their classes' names, so they can
+still be linked to the native library. The following additional option will
+ensure that:
+<pre>
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+</pre>
+<p>
+Note the use of <code>-keepclasseswithmembernames</code>. We don't want to
+preserve all classes or all native methods; we just want to keep the relevant
+names from being obfuscated.
+
+<a name="enumerations"> </a>
+<h3>Processing enumeration classes</h3>
+If your application, applet, servlet, library, etc., contains enumeration
+classes, you'll have to preserve a special method. Enumerations were
+introduced in JDK 5.0. The <code>javac</code> compiler will translate
+enumerations into classes with a special structure. The classes contain
+implementations of a method that the run-time environment accesses by
+introspection (Isn't that just grand? Introspection is the self-modifying code
+of a new generation). You'll therefore have to specify it explicitly:
+<pre>
+-keepclassmembers class * extends java.lang.Enum {
+    public **[] values();
+}
+</pre>
+
+<a name="serializable"> </a>
+<h3>Processing serializable classes</h3>
+More complex applications, applets, servlets, libraries, etc., may contain
+classes that are serialized. Depending on the way in which they are used, they
+may require special attention:
+<ul>
+
+<li>Often, serialization is simply a means of transporting data, without
+    long-term storage. Classes that are shrunk and obfuscated should then
+    continue to function fine with the following additional directives:
+
+<pre>
+-keepclassmembers class * implements java.io.Serializable {
+    private void writeObject(java.io.ObjectOutputStream);
+    private void readObject(java.io.ObjectInputStream);
+    java.lang.Object writeReplace();
+    java.lang.Object readResolve();
+}
+</pre>
+<p>
+
+    The <code>-keepclassmembers</code> option makes sure that any
+    serialization methods are kept. By using this directive instead of the
+    basic <code>-keep</code> directive, we're not forcing preservation of
+    <i>all</i> serializable classes, just preservation of the listed members
+    of classes that are actually used.
+    <p>
+
+<li>Sometimes, the serialized data are stored, and read back later into newer
+    versions of the serializable classes. One then has to take care the classes
+    remain compatible with their unprocessed versions and with future
+    processed versions. In such cases, the relevant classes will most likely
+    have <code>serialVersionUID</code> fields. The following directives should
+    then be sufficient to ensure compatibility over time:
+
+<pre>
+-keepnames class * implements java.io.Serializable
+
+-keepclassmembers class * implements java.io.Serializable {
+    static final long serialVersionUID;
+    !static !transient <fields>;
+    private void writeObject(java.io.ObjectOutputStream);
+    private void readObject(java.io.ObjectInputStream);
+    java.lang.Object writeReplace();
+    java.lang.Object readResolve();
+}
+</pre>
+<p>
+
+    The <code>serialVersionUID</code> line makes sure that field is preserved.
+    The <code><fields></code> line preserves all non-static,
+    non-transient fields, with their original names. The introspection of the
+    serialization process and the de-serialization process will then find
+    consistent names.
+
+<li>Occasionally, the serialized data have to remain compatible, but the
+    classes involved lack <code>serialVersionUID</code> fields. I imagine the
+    original code will then be hard to maintain, since the serial version UID
+    is then computed from a list of features the serializable class. Changing
+    the class ever so slightly may change the computed serial version UID. The
+    list of features is specified in the chapter on <a
+    href="http://java.sun.com/j2se/1.4/docs/guide/serialization/spec/class.html#wp4100">Stream
+    Unique Identifiers</a> of Sun's <a
+    href="http://java.sun.com/j2se/1.4/docs/guide/serialization/">Object
+    Serialization Guide</a>. The following directives should at least
+    partially ensure compatibility with the original classes
+
+<pre>
+-keepnames class * implements java.io.Serializable
+
+-keepclassmembers class * implements java.io.Serializable {
+    static final long serialVersionUID;
+    !static !transient <fields>;
+    !private <fields>;
+    !private <methods>;
+    private void writeObject(java.io.ObjectOutputStream);
+    private void readObject(java.io.ObjectInputStream);
+    java.lang.Object writeReplace();
+    java.lang.Object readResolve();
+}
+</pre>
+<p>
+
+    The new directives force preservation of the elements involved in the UID
+    computation. In addition, the user will have to manually specify all
+    interfaces of the serializable classes (using something like "<code>-keep
+    interface MyInterface</code>"), since these names are also used when
+    computing the UID. A fast but sub-optimal alternative would be simply
+    keeping all interfaces with "<code>-keep interface *</code>".
+
+</ul>
+<p>
+
+Note that the above directives may preserve more classes and class members
+than strictly necessary. For instance, a large number of classes may implement
+the <code>Serialization</code> interface, yet only a small number may actually
+ever be serialized. Knowing your application and tuning the configuration will
+often produce more compact results.
+
+<a name="beans"> </a>
+<h3>Processing bean classes</h3>
+If your application, applet, servlet, library, etc., uses extensive
+introspection on bean classes to find bean editor classes, or getter and
+setter methods, then configuration may become laborious. There's not much else
+you can do than making sure the bean class names, or the getter and setter
+names don't change. For instance:
+<pre>
+-keep public class mypackage.MyBean {
+    public void setMyProperty(int);
+    public int getMyProperty();
+}
+
+-keep public class mypackage.MyBeanEditor
+</pre>
+<p>
+If there are too many elements to list explicitly, wildcards in class names
+and method signatures might be helpful. This example encompasses a wide range
+of setters and getters:
+<pre>
+-keep class mybeans.** {
+    void set*(%);
+    void set*(**);
+    void set*(%[]);
+    void set*(**[]);
+    void set*(int, %);
+    void set*(int, **);
+
+    %    get*();
+    **   get*();
+    %[]  get*();
+    **[] get*();
+    %    get*(int);
+    **   get*(int);
+}
+</pre>
+<p>
+The '<code>%</code>' wildcard matches any primitive type, while the
+'<code>**</code>' wildcard matches any class name. Array types  have to be
+specified explicitly. This results in a short list of alternative method
+signatures.
+
+<a name="annotations"> </a>
+<h3>Processing annotations</h3>
+As of JDK 5.0, class files can contain annotations. Annotations are
+represented by attributes that have no direct effect on the execution of the
+code. However, their values can be retrieved through introspection, allowing
+developers to adapt the execution behavior accordingly. By default, ProGuard
+treats annotation attributes as optional, and removes them in the obfuscation
+step. If they are required, you'll have to specify this explicitly:
+<pre>
+-keepattributes *Annotation*
+</pre>
+<p>
+For brevity, we're specifying a wildcarded attribute name, which will match
+<code>RuntimeVisibleAnnotations</code>,
+<code>RuntimeInvisibleAnnotations</code>,
+<code>RuntimeVisibleParameterAnnotations</code>,
+<code>RuntimeInvisibleParameterAnnotations</code>, and
+<code>AnnotationDefault</code>. Depending on the purpose of the processed
+code, you could refine this selection, for instance not keeping the run-time
+invisible annotations (which are only used at compile-time).
+<p>
+Some code may make further use of introspection to figure out the enclosing
+methods of anonymous inner classes. In that case, the corresponding attribute
+will have to be preserved as well:
+<pre>
+-keepattributes EnclosingMethod
+</pre>
+
+<a name="rmi"> </a>
+<h3>Processing RMI code</h3>
+Reportedly, the easiest way to handle RMI code is to process the code with
+ProGuard first and then invoke the <code>rmic</code> tool. If that is not
+possible, you may want to try something like this:
+<pre>
+-keep interface * extends java.rmi.Remote {
+    <methods>;
+}
+
+-keep class * implements java.rmi.Remote {
+    <init>(java.rmi.activation.ActivationID, java.rmi.MarshalledObject);
+}
+</pre>
+<p>
+The first directive keeps all your Remote interfaces and their methods. The
+second one keeps all the implementations, along with their particular RMI
+constructors, if any.
+
+<a name="stacktrace"> </a>
+<h3>Producing useful obfuscated stack traces</h3>
+These options let obfuscated applications or libraries produce stack traces
+that can still be deciphered later on:
+<pre>
+-printmapping out.map
+
+-renamesourcefileattribute SourceFile
+-keepattributes SourceFile,LineNumberTable
+</pre>
+<p>
+We're keeping all source file attributes, but we're replacing their values by
+the string "SourceFile". We could use any string. This string is already
+present in all class files, so it doesn't take up any extra space. If you're
+working with J++, you'll want to keep the "SourceDir" attribute as well.
+<p>
+We're also keeping the line number tables of all methods.
+<p>
+Whenever both of these attributes are present, the Java run-time environment
+will include line number information when printing out exception stack traces.
+<p>
+The information will only be useful if we can map the obfuscated names back to
+their original names, so we're saving the mapping to a file
+<code>out.map</code>. The information can then be used by the <a
+href="retrace/index.html">ReTrace</a> tool to restore the original stack trace.
+
+<a name="multiple"> </a>
+<h3>Processing multiple applications at once</h3>
+You can process several independent applications (or applets, midlets,...) in
+one go, in order to save time and effort. ProGuard's input and output handling
+offers various ways to keep the output nicely structured.
+<p>
+The easiest way is to specify your input jars (and/or wars, ears, zips, and
+directories) and a single output directory. ProGuard will then reconstruct the
+input in this directory, using the original jar names. For example, showing
+just the input and output options:
+<pre>
+-injars  application1.jar
+-injars  application2.jar
+-injars  application3.jar
+-injars  application.war
+-outjars processed_applications
+</pre>
+<p>
+After processing, the directory <code>processed_applications</code> will
+contain the processed application jars and war, with their original names.
+<p>
+If you want explicit control over the output names, or if you want to combine
+input jars (and/or wars, ears, zips, or directories) into output jars (and/or
+wars, ears, zips, or directories), you can group the <code>-injars</code> and
+<code>-outjars</code> options. For example:
+<pre>
+-injars  application1.jar
+-injars  application2.jar
+-injars  application3.jar
+-outjars application_out1.war
+
+-injars  application.war
+-outjars application_out2.war
+</pre>
+<p>
+This configuration creates two wars: one containing all processed application
+jars, and one containing the processed contents of the input war.
+<p>
+Note that ProGuard will try to package output archives in a sensible way,
+reconstructing the input entries as much as required. If you want even greater
+control, you can add filters to the input and the output, filtering out zips,
+ears, wars, jars, and/or ordinary files.
+
+<a name="incremental"> </a>
+<h3>Incremental obfuscation</h3>
+After having <a href="#application">processed an application</a>, e.g.
+ProGuard itself, you can still incrementally add other pieces of code that
+depend on it, e.g. the ProGuard GUI:
+<pre>
+-injars       proguardgui.jar
+-outjars      proguardgui_out.jar
+-libraryjars  <java.home>/lib/rt.jar
+-libraryjars  proguard.jar
+-applymapping proguard.map
+
+-keep public class proguard.gui.ProGuardGUI {
+    public static void main(java.lang.String[]);
+}
+</pre>
+<p>
+We're reading both unprocessed jars: the new one as the input jar, and the old
+one as a library jar. The <code>-applymapping</code> option then makes sure
+the ProGuard part of the code gets the previously produced obfuscation
+mapping. The final application will consist of the obfuscated ProGuard jar and
+the additional obfuscated GUI jar.
+
+<a name="deadcode"> </a>
+<h3>Finding dead code</h3>
+These options list unused fields and methods in the application
+<code>mypackage.MyApplication</code>:
+<pre>
+-injars      in.jar
+-libraryjars <java.home>/lib/rt.jar
+-dontoptimize
+-dontobfuscate
+-printusage
+
+-keep public class mypackage.MyApplication {
+    public static void main(java.lang.String[]);
+}
+</pre>
+<p>
+We're not specifying an output jar, just printing out some results.
+<p>
+We're saving a little bit of time by skipping the optimization and obfuscation
+steps.
+
+<a name="structure"> </a>
+<h3>Printing out the internal structure of class files</h3>
+These options print out the internal structure of all class files in the input
+jar:
+<pre>
+-injars in.jar
+-dontshrink
+-dontoptimize
+-dontobfuscate
+-dump
+</pre>
+<p>
+Note how we don't need to specify the Java run-time jar, because we're not
+processing the input jar at all.
+<p>
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
diff --git a/docs/manual/gui.html b/docs/manual/gui.html
new file mode 100644
index 0000000..7880b6e
--- /dev/null
+++ b/docs/manual/gui.html
@@ -0,0 +1,418 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard GUI</title>
+</head>
+<body>
+
+<h2>Graphical User Interface</h2>
+
+To run the ProGuard graphical user interface, just type:
+<p class="code">
+<code><b>java -jar proguardgui.jar</b> [-nosplash] </code>[<i>configuration_file</i>]
+</p>
+The GUI will pop up in a window. With the <code>-nosplash</code> option, you
+can switch off the short opening animation. If you have specified a ProGuard
+configuration file, it will be loaded. The GUI works like a wizard. You can
+edit the configuration and execute ProGuard through a few tabs:
+<p>
+
+<table cellspacing="5" cellpadding="5">
+<tr><td class="button"><a href="#proguard">ProGuard</a></td>
+    <td>Load an existing configuration file.</td></tr>
+<tr><td class="button"><a href="#inputoutput">Input/Output</a></td>
+    <td>Specify the program jars and library jars.</td></tr>
+<tr><td class="button"><a href="#shrinking">Shrinking</a></td>
+    <td>Specify the shrinking options.</td></tr>
+<tr><td class="button"><a href="#obfuscation">Obfuscation</a></td>
+    <td>Specify the obfuscation options.</td></tr>
+<tr><td class="button"><a href="#optimization">Optimization</a></td>
+    <td>Specify the optimization options.</td></tr>
+<tr><td class="button"><a href="#information">Information</a></td>
+    <td>Specify some options to get information.</td></tr>
+<tr><td class="button"><a href="#process">Process</a></td>
+    <td>View and save the resulting configuration, and run ProGuard.</td></tr>
+</table>
+<p>
+
+In addition, there is a tab to execute ReTrace interactively:
+<p>
+
+<table cellspacing="5" cellpadding="5">
+<tr><td class="button"><a href="#retrace">ReTrace</a></td>
+    <td>Set up and run ReTrace, to de-obfuscate stack traces.</td></tr>
+</table>
+<p>
+
+You can freely toggle between the tabs by means of the buttons on the left-hand
+side of the window, or by means of the <b>Previous</b> and <b>Next</b> buttons
+at the bottom of the tabs.
+<p>
+
+<a name="proguard"> </a>
+<h2>The ProGuard Tab</h2>
+
+The <i>ProGuard</i> tab presents a welcome message and one important button at
+the bottom:
+<p>
+
+<table cellspacing="5" cellpadding="5">
+<tr><td class="button">Load configuration...</td>
+    <td>opens a file chooser to load an existing ProGuard configuration
+    file.</td></tr>
+</table>
+<p>
+
+If you don't want to load an existing configuration, you can just continue
+creating a new configuration from scratch.
+<p>
+
+<a name="inputoutput"> </a>
+<h2>The Input/Output Tab</h2>
+
+The <i>Input/Output</i> tab contains two lists, respectively to specify the
+program jars (or wars, ears, zips, or directories), and the library jars (or
+wars, ears, zips, or directories).
+
+<ul>
+<li>The list of program jars contains input entries and output entries. Input
+    entries contain the class files and resource files to be processed. Output
+    entries specify the destinations to which the processed results will be
+    written. They are preceded by arrows, to distinguish them from input
+    entries. The results of each consecutive list of input entries will be
+    written to the subsequent consecutive list of output entries.
+
+<li>The library jars are not copied to the output jars; they contain class
+    files that are used by class files in the program jars and that are
+    necessary for correct processing. This list typically at least contains the
+    targeted Java runtime jar.
+</ul>
+<p>
+
+Each of these lists can be edited by means of a couple of buttons on the
+right-hand side:
+<p>
+
+<table cellspacing="5" cellpadding="5">
+<tr><td class="button">Add input...</td> <td>opens a file chooser to add an
+    input entry to the list of program jars.</td></tr>
+<tr><td class="button">Add output...</td> <td>opens a file chooser to add an
+    output entry to the list of program jars.</td></tr>
+<tr><td class="button">Add...</td>
+    <td>opens a file chooser to add an entry to the list of library
+    jars.</td></tr>
+<tr><td class="button">Edit...</td>
+    <td>opens a file chooser to edit the selected entry in the list.</td></tr>
+<tr><td class="button">Filter...</td>
+    <td>opens a text entry field to add or edit the filters of the selected
+    entries in the list.</td></tr>
+<tr><td class="button">Remove</td>
+    <td>removes the selected entries from the list.</td></tr>
+<tr><td class="button">Move up</td>
+    <td>moves the selected entries one position up the list.</td></tr>
+<tr><td class="button">Move down</td>
+    <td>moves the selected entries one position down the list.</td></tr>
+<tr><td class="button">Move to libraries</td>
+    <td>moves the selected entries in the list of program jars to the list of
+    library jars.</td></tr>
+<tr><td class="button">Move to program</td>
+    <td>moves the selected entries in the list of library jars to the list of
+    program jars.</td></tr>
+</table>
+<p>
+
+Filters allow to filter files based on their names. One can specify filters
+for class file names and resource file names, for jar file names, for war file
+names, for ear file names, and for zip file names. Multiple entries in the
+program list only make sense when combined with filters; each output file is
+written to the first entry with a matching filter.
+<p>
+
+Input entries that are currently not readable are colored red.
+<p>
+
+The order of the entries in each list may matter, as the first occurrence of
+any duplicate entries gets precedence, just as in conventional class paths.
+<p>
+
+Corresponding configuration options:
+<ul type="none">
+<li>-<a href="usage.html#injars">injars</a>
+<li>-<a href="usage.html#outjars">outjars</a>
+<li>-<a href="usage.html#libraryjars">libraryjars</a>
+<li><a href="usage.html#classpath"><i>class_path</i></a>
+<li><a href="usage.html#filters"><i>filters</i></a>
+</ul>
+<p>
+
+<a name="shrinking"> </a>
+<h2>The Shrinking Tab</h2>
+
+The <i>Shrinking</i> tab presents a number of options that affect the
+shrinking step. The basic options are followed by a few lists of classes and
+class members (fields and methods) that must be protected from shrinking (and
+implicitly from obfuscation as well).
+<p>
+
+The fixed lists contain predefined entries that are typically useful for many
+applications. Each of these entries can be toggled by means of a check box.
+The text field following each entry allows to constrain the applicable classes
+by means of a comma-separated list of wildcarded, fully-qualified class
+names. The default is "*", which means that all input classes of the
+corresponding type are considered.
+<p>
+
+For example, checking the <b>Applications</b> entry and filling in
+"myapplications.**" after it would mean: keep all classes that have a main
+method in the "myapplications" package and any of its subpackages.
+<p>
+
+The variable list at the bottom allows to define additional entries
+yourself. The list can be edited by means of a couple of buttons on the
+right-hand side:
+<p>
+
+<table cellspacing="5" cellpadding="5">
+<tr><td class="button">Add...</td>
+    <td>opens a window to add a new entry to the list.</td></tr>
+<tr><td class="button">Edit...</td>
+    <td>opens a window to edit the selected entry in the list.</td></tr>
+<tr><td class="button">Remove</td>
+    <td>removes the selected entries from the list.</td></tr>
+<tr><td class="button">Move up</td>
+    <td>moves the selected entries one position up the list.</td></tr>
+<tr><td class="button">Move down</td>
+    <td>moves the selected entries one position down the list.</td></tr>
+</table>
+<p>
+
+The interface windows allow to specify classes, fields, and methods. They
+contain text fields and check boxes to constrain these items. They have
+<b>Ok</b> and <b>Cancel</b> buttons to apply or to cancel the operation.
+<p>
+
+For example, your application may be creating some classes dynamically using
+<code>Class.forName</code>. You should then specify them here, so they are kept
+by their original names. Press the <b>Add...</b> button to open the class
+window. Fill out the fully-qualified class name in the <b>Code</b> text field,
+and press the <b>Ok</b> button. Repeat this for all required classes. Wildcards
+can be helpful to specify a large number of related classes in one go. If you
+want to specify all implementations of a certain interface, fill out the
+fully qualified interface name in the <b>Extends/implements class</b> instead.
+<p>
+
+For more advanced settings, it is advisable to become familiar with ProGuard's
+configuration options through the <a href="usage.html">Usage section</a> and
+the <a href="examples.html">Examples section</a>.  We'll suffice with a brief
+overview of the three dialogs provided by the GUI.
+<p>
+
+The <i>keep class</i> dialog appears when adding or editing new special keep
+entries. It has text fields and selections for specifying and constraining
+classes and class members to keep.
+
+<ul>
+<li>The <b>Comments</b> text field allows to add optional comments to this
+    entry. The comments will identify the entry in the list and they will
+    appear as comments in the configuration file.
+
+<li>The <b>Keep</b> selection allows to specify whether you want to protect
+    the specified classes and their specified class members, or just the
+    specified class members from the specified classes, or the specified
+    classes and the specified class members, if the class members are present.
+    Note that class members will only be protected if they are explicitly
+    specified, even if only by means of a wildcard.
+
+<li>The <b>Access</b> selections allows to specify constraints on the class or
+    classes, based on their access modifiers.
+
+<li>The <b>Class</b> text field takes the fully-qualified name of the class or
+    classes. The class name can contain wildcards.
+
+<li>The <b>Extends/implements class</b> text field takes the fully-qualified
+    name of the class or interface that the above classes must extend.
+
+<li>The <b>Class members</b> list allows to specify a list of fields and
+    methods to keep. It can be edited by means of a list of buttons on the
+    right-hand side.
+</ul>
+<p>
+
+The <i>keep field</i> dialog appears when adding or editing fields within the
+above dialog. It has text fields and selections for specifying and constraining
+fields to keep.
+
+<ul>
+<li>The <b>Access</b> selections allows to specify constraints on the field or
+    fields, based on their access modifiers.
+
+<li>The <b>Return type</b> text field takes the fully-qualified type of the
+    field or fields. The type can contain wild-cards if it is a class name.
+
+<li>The <b>Name</b> text field takes the name of the field or fields. The field
+    name can contain wildcards.
+</ul>
+<p>
+
+Similarly, the <i>keep method</i> dialog appears when adding or editing methods
+within the keep class dialog. It has text fields and selections for specifying
+and constraining methods to keep.
+
+<ul>
+<li>The <b>Access</b> selections allows to specify constraints on the method or
+    methods, based on their access modifiers.
+
+<li>The <b>Return type</b> text field takes the fully-qualified type of the         method or methods. The type can contain wild-cards if it is a class name.
+
+<li>The <b>Name</b> text field takes the name of the method or methods. The
+    method name can contain wildcards.
+
+<li>The <b>Arguments</b> text field takes the comma-separated list of
+    fully-qualified method arguments. Each of these arguments can contain
+    wildcards, if it is a class name.
+</ul>
+<p>
+
+Corresponding configuration options:
+<ul type="none">
+<li>-<a href="usage.html#dontshrink">dontshrink</a>
+<li>-<a href="usage.html#printusage">printusage</a>
+<li>-<a href="usage.html#keep">keep</a>
+<li>-<a href="usage.html#keepclassmembers">keepclassmembers</a>
+<li>-<a href="usage.html#keepclasseswithmembers">keepclasseswithmembers</a>
+</ul>
+<p>
+
+<a name="obfuscation"> </a>
+<h2>The Obfuscation Tab</h2>
+
+The <i>Obfuscation</i> tab presents a number of options that affect the
+obfuscation step. The basic options are followed by a few lists of classes and
+class members (fields and methods) that must be protected from obfuscation
+(but not necessarily from shrinking).
+<p>
+
+The lists are manipulated in the same way as in the <a
+href="#shrinking">Shrinking Tab</a>.
+<p>
+
+Corresponding configuration options:
+<ul type="none">
+<li>-<a href="usage.html#dontobfuscate">dontobfuscate</a>
+<li>-<a href="usage.html#printmapping">printmapping</a>
+<li>-<a href="usage.html#applymapping">applymapping</a>
+<li>-<a href="usage.html#obfuscationdictionary">obfuscationdictionary</a>
+<li>-<a href="usage.html#overloadaggressively">overloadaggressively</a>
+<li>-<a href="usage.html#defaultpackage">defaultpackage</a>
+<li>-<a href="usage.html#dontusemixedcaseclassnames">dontusemixedcaseclassnames</a>
+<li>-<a href="usage.html#keepattributes">keepattributes</a>
+<li>-<a href="usage.html#renamesourcefileattribute">renamesourcefileattribute</a>
+<li>-<a href="usage.html#keepnames">keepnames</a>
+<li>-<a href="usage.html#keepclassmembernames">keepclassmembernames</a>
+<li>-<a href="usage.html#keepclasseswithmembernames">keepclasseswithmembernames</a>
+<li><a href="usage.html#classspecification"><i>class_specification</i></a>
+</ul>
+<p>
+
+<a name="optimization"> </a>
+<h2>The Optimization Tab</h2>
+
+The <i>Optimization</i> tab presents a number of options that affect the
+optimization step. The basic options are followed by a few lists of class
+method calls that can be removed if ProGuard can determine that their results
+are not being used.
+<p>
+
+The lists are manipulated in much the same way as in the <a
+href="#shrinking">Shrinking Tab</a>.
+<p>
+
+Corresponding configuration options:
+<ul type="none">
+<li>-<a href="usage.html#dontoptimize">dontoptimize</a>
+<li>-<a href="usage.html#allowaccessmodification">allowaccessmodification</a>
+<li>-<a href="usage.html#assumenosideeffects">assumenosideeffects</a>
+<li><a href="usage.html#classspecification"><i>class_specification</i></a>
+</ul>
+<p>
+
+<a name="information"> </a>
+<h2>The Information Tab</h2>
+
+The <i>Information</i> tab presents a number of options that affect the
+information that ProGuard returns when processing your code.
+<p>
+
+Corresponding configuration options:
+<ul type="none">
+<li>-<a href="usage.html#printseeds">printseeds</a>
+<li>-<a href="usage.html#verbose">verbose</a>
+<li>-<a href="usage.html#dontnote">dontnote</a>
+<li>-<a href="usage.html#dontwarn">dontwarn</a>
+<li>-<a href="usage.html#ignorewarnings">ignorewarnings</a>
+<li>-<a href="usage.html#dontskipnonpubliclibraryclasses">dontskipnonpubliclibraryclasses</a>
+<li>-<a href="usage.html#dontskipnonpubliclibraryclassmembers">dontskipnonpubliclibraryclassmembers</a>
+</ul>
+<p>
+
+<a name="process"> </a>
+<h2>The Process Tab</h2>
+
+The <i>Process</i> tab has an output console for displaying the configuration
+and the messages while processing. There are three important buttons at the
+bottom:
+<p>
+
+<table cellspacing="5" cellpadding="5">
+<tr><td class="button">View configuration</td>
+    <td>displays the current ProGuard configuration in the console.</td></tr>
+<tr><td class="button">Save configuration...</td>
+    <td>opens a file chooser to save the current ProGuard
+    configuration.</td></tr>
+<tr><td class="button">Process!</td>
+    <td>executes ProGuard with the current configuration.</td></tr>
+</table>
+<p>
+
+<a name="retrace"> </a>
+<h2>The ReTrace Tab</h2>
+
+The <i>ReTrace</i> tab has a panel with a few settings, an input text area for
+the obfuscated stack trace, and an output console to view the de-obfuscated
+stack trace:
+
+<ul>
+<li>The <b>Mapping file</b> text field takes the name of the required mapping
+    file that ProGuard wrote while processing the original code. The file name
+    can be entered manually or by means of the <b>Browse...</b> button that
+    opens a file chooser.
+
+<li>The <b>Verbose</b> check box in the settings panel allows to toggle between
+    normal mode and verbose mode.
+
+<li>The <b>Obfuscated stack trace</b> text area allows to enter the stack
+    trace, typically by copying and pasting it from elsewhere. Alternatively,
+    it can be loaded from a file by means of the load button below.
+</ul>
+
+There are two buttons at the bottom:
+<p>
+
+<table cellspacing="5" cellpadding="5">
+<tr><td class="button">Load stack trace...</td>
+    <td>opens a file chooser to load an obfuscated stack trace.</td></tr>
+<tr><td class="button">ReTrace!</td>
+    <td>executes ReTrace with the current settings.</td></tr>
+</table>
+<p>
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
diff --git a/docs/manual/index.html b/docs/manual/index.html
new file mode 100644
index 0000000..a4c9ee9
--- /dev/null
+++ b/docs/manual/index.html
@@ -0,0 +1,39 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard Manual</title>
+</head>
+<body>
+
+<h2>ProGuard</h2>
+
+<ol>
+<li><a href="introduction.html">Introduction</a>
+<li><a href="usage.html">Usage</a>
+<li><a href="limitations.html">Limitations</a>
+<li><a href="examples.html">Examples</a>
+<li><a href="troubleshooting.html">Troubleshooting</a>
+<li><a href="refcard.html">Reference Card</a>
+<li><a href="gui.html">Graphical User Interface</a>
+<li><a href="ant.html">Ant Task</a>
+<li><a href="wtk.html">J2ME Wireless Toolkit Integration</a>
+</ol>
+
+<h2>ReTrace</h2>
+
+<ol>
+<li><a href="retrace/introduction.html">Introduction</a>
+<li><a href="retrace/usage.html">Usage</a>
+<li><a href="retrace/examples.html">Examples</a>
+</ol>
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
diff --git a/docs/manual/introduction.html b/docs/manual/introduction.html
new file mode 100644
index 0000000..f8e939e
--- /dev/null
+++ b/docs/manual/introduction.html
@@ -0,0 +1,116 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard Introduction</title>
+</head>
+<body>
+
+<h2>Introduction</h2>
+
+<b>ProGuard</b> is a Java class file shrinker, optimizer, and obfuscator. The
+shrinking step detects and removes unused classes, fields, methods, and
+attributes. The optimization step analyzes and optimizes the bytecode of the
+methods. The obfuscation step renames the remaining classes, fields, and
+methods using short meaningless names. The resulting jars are smaller and
+harder to reverse-engineer.
+<p>
+ProGuard can also be used to list unused fields and methods in an application,
+and to print out the internal structure of class files.
+<p>
+
+<table class="diagram" align="center">
+
+<tr>
+<td rowspan="4" class="lightblock">Input jars</td>
+<td colspan="6" class="transparentblock"></td>
+</tr>
+
+<tr>
+<td rowspan="2" class="transparentblock"></td>
+<td rowspan="3" class="lightblock">Shrunk code</td>
+<td colspan="4" class="transparentblock"></td>
+</tr>
+
+<tr>
+<td             class="transparentblock"></td>
+<td rowspan="2" class="lightblock">Optimized code</td>
+<td colspan="2" class="transparentblock"></td>
+</tr>
+
+<tr>
+<td             class="transparentblock">- shrink →</td>
+<td             class="transparentblock">- optimize →</td>
+<td             class="transparentblock">- obfuscate →</td>
+<td             class="lightblock">Output jars</td>
+</tr>
+
+<tr>
+<td             class="darkblock">Library jars</td>
+<td colspan="5" class="transparentblock">----------------- (unchanged) -----------------→</td>
+<td             class="darkblock">Library jars</td>
+</tr>
+
+</table>
+<p>
+
+ProGuard typically reads the <b>input jars</b> (or wars, ears, zips, or
+directories). It then shrinks, optimizes, and obfuscates them. It then writes
+the results to one or more <b>output jars</b> (or wars, ears, zips, or
+directories). The input jars can optionally contain resource files. ProGuard
+copies all non-class resource files from the input jars to the output jars.
+Their names and contents remain unchanged.
+<p>
+ProGuard requires the <b>library jars</b> (or wars, ears, zips, or
+directories) of the input jars to be specified. It can then reconstruct class
+hierarchies and other class dependencies, which are necessary for proper
+shrinking, optimization, and obfuscation. The library jars themselves always
+remain unchanged. You should still put them in the class path of your final
+application.
+<p>
+In order to determine which code has to be preserved and which code can be
+discarded or obfuscated, you have to specify one or more entry points to your
+code. These entry points are typically classes with main methods, applets,
+midlets, etc.
+<ul>
+<li>In the <b>shrinking step</b>, ProGuard starts from these seeds and
+    recursively determines which classes and class members are used. All other
+    classes and class members are discarded.
+
+<li>In the <b>optimization step</b>, ProGuard further optimizes the code.
+    Among other optimizations, classes and methods that are not entry points
+    can be made final, and some methods may be inlined.
+<li>In the <b>obfuscation step</b>, ProGuard renames classes and class members
+    that are not entry points. In this entire process, keeping the entry
+    points ensures that they can still be accessed by their original names.
+</ul>
+<p>
+Any classes or class members of your code that are created or invoked
+dynamically (that is, by name) have to be specified as entry points too. It is
+generally impossible to determine these cases automatically, but ProGuard will
+offer some suggestions if keeping some classes or class members appears
+necessary. For proper results, you should at least be somewhat familiar with
+the code that you are processing.
+<p>
+ProGuard does handle <code>Class.forName("SomeClass")</code> and
+<code>SomeClass.class</code> constructs automatically. The referenced classes
+are preserved in the shrinking phase, and the string arguments are properly
+replaced in the obfuscation phase. With variable string arguments, it is
+generally impossible to determine their possible values (they might be read
+from a configuration file, for instance). However, as mentioned, ProGuard will
+note constructs like
+"<code>(SomeClass)Class.forName(variable).newInstance()</code>". These might
+be an indication that the class or interface <code>SomeClass</code> and/or its
+implementations may need to be preserved. You can then adapt your configuration
+accordingly.
+<p>
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
diff --git a/docs/manual/limitations.html b/docs/manual/limitations.html
new file mode 100644
index 0000000..b69544f
--- /dev/null
+++ b/docs/manual/limitations.html
@@ -0,0 +1,95 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard Limitations</title>
+</head>
+<body>
+
+<h2>Limitations</h2>
+
+When using ProGuard, you should be aware of a few issues, all of which are
+easily avoided or resolved:
+<p>
+<ul>
+<li>ProGuard handles the essential elements of Java: classes, interfaces, class
+    members, inheritance,... ProGuard itself is not aware of any additional
+    <b>naming conventions or APIs</b>: main methods, native methods, beans,
+    serialization,... You'll have to specify these in your configuration. The
+    configuration syntax is intended to be compact, readable, expressive, and
+    generally applicable. It's usually quite simple. The examples section of
+    this manual provides many typical examples. The graphical user interface
+    has checkboxes for common boilerplate configurations.
+    <p>
+
+<li>ProGuard currently copies <b>manifest files and resource files</b>
+    unchanged. Directory entries, on the other hand, are not copied to the
+    output jars. If your code has any special dependencies on these elements,
+    you should modify or add them manually.
+    <p>
+
+<li>For efficiency, ProGuard always ignores any <b>private or package visible
+    library classes</b> while reading library jars. If any of them are
+    extended by public library classes, and then extended again by input
+    classes, ProGuard will complain it can't find them. In that case, you'll
+    have to use the <code>-dontskipnonpubliclibraryclasses</code> option. The
+    graphical user interface has a checkbox for this setting.
+    <p>
+
+<li>For best results, ProGuard's <b>optimization</b> algorithms assume that
+    the processed code never intentionally causes NullPointerExceptions and
+    ArrayIndexOutOfBoundsExceptions to achieve something useful. For instance,
+    it may remove a method call <code>myObject.myMethod()</code> if that call
+    wouldn't have any effect. It ignores that <code>myObject</code> might be
+    null, causing a NullPointerException. In some way this is a good thing:
+    optimized code may throw fewer exceptions. Should this entire assumption be
+    false, you'll have to switch off optimization using the
+    <code>-dontoptimize</code> option.
+    <p>
+
+<li>If an input jar and a library jar contain classes in the <b>same
+    package</b>, the obfuscated output jar may contain class names that
+    overlap with class names in the library jar. This is most likely if the
+    library jar has been obfuscated before, as it will then probably contain
+    classes named 'a', 'b', etc. Packages should therefore never be split
+    across input jars and library jars.
+    <p>
+
+<li>ProGuard may not handle <b>obfuscation marker interfaces</b> as expected.
+    If you specify "<code>-keep class * implements MyKeepInterface</code>",
+    and <code>MyKeepInterface</code> is not used in your code, the specified
+    classes are kept, but they are obfuscated. Technically, the interface is
+    removed in the shrinking phase, making the directive void in the
+    obfuscation phase. This behavior may be fixed in the future. For now, you
+    can get around it by explicitly keeping the interface as well:
+    "<code>-keep class MyKeepInterface</code>". In any case, creating a proper
+    configuration file seems a cleaner solution than using such an obfuscation
+    marker interface.
+    <p>
+
+<li>ProGuard's obfuscation process currently doesn't follow the <b>naming
+    rule</b> specifying that internal classes must be named as
+    <code>ExternalClass$InternalClass</code>, for instance (cfr. <a href=
+    "http://java.sun.com/docs/books/jls/second_edition/html/j.title.doc.html"
+    >The Java Language Specification, Second Edition</a>, <a href=
+    "http://java.sun.com/docs/books/jls/second_edition/html/binaryComp.doc.html#59892"
+    >Section 13.1</a>). This should not present a problem in practice, since
+    the rule is mainly intended for transformations at the source code level.
+    Internal-external class relationships are still represented correctly
+    inside the binary class files. Decompilers or others tools that rely on
+    the naming rule may have problems processing obfuscated jars. I'm not
+    aware of any such cases.
+    <p>
+
+</ul>
+<p>
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
diff --git a/docs/manual/refcard.html b/docs/manual/refcard.html
new file mode 100644
index 0000000..cf10b47
--- /dev/null
+++ b/docs/manual/refcard.html
@@ -0,0 +1,305 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard Reference Card</title>
+</head>
+<body>
+
+<h1>ProGuard Reference Card</h1>
+
+<h2>Usage</h2>
+
+<code><b>java -jar proguard.jar </b></code><i>options</i> ...
+<p>
+  Typically:
+<p>
+<code><b>java -jar proguard.jar @myconfig.pro</b></code>
+<p>
+
+<h2>Options</h2>
+
+<table cellspacing="10">
+
+<tr>
+<td valign="top"><code><b>@</b></code><i>filename</i></td>
+
+<td>Short for '<code>-include</code> <i>filename</i>'.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-include</b></code> <i>filename</i></td>
+
+<td>Read configuration options from the given file.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-injars</b></code> <i>class_path</i></td>
+<td>Specifies the program jars (or wars, ears, zips, or directories).</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-outjars</b></code> <i>class_path</i></td>
+<td>Specifies the name of the output jars (or wars, ears, zips, or
+    directories).</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-libraryjars</b></code> <i>class_path</i></td>
+<td>Specifies the library jars (or wars, ears, zips, or directories).</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-dontskipnonpubliclibraryclasses</b></code></td>
+<td>Don't ignore non-public library classes.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-dontskipnonpubliclibraryclassmembers</b></code></td>
+<td>Don't ignore package visible library class members.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-keep</b></code> <i>class_specification</i></td>
+<td>Preserve the specified classes <i>and</i> class members.</td>
+
+</tr>
+<tr>
+<td valign="top"><code><b>-keepclassmembers</b></code>
+    <i>class_specification</i></td>
+<td>Preserve the specified class members, if their classes are preserved as
+    well.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-keepclasseswithmembers</b></code>
+    <i>class_specification</i></td>
+<td>Preserve the specified classes <i>and</i> class members, if all of the
+    specified class members are present.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-keepnames</b></code>
+    <i>class_specification</i></td>
+<td>Preserve the names of the specified classes <i>and</i> class members (if
+    they aren't removed in the shrinking step).</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-keepclassmembernames</b></code>
+    <i>class_specification</i></td>
+<td>Preserve the names of the specified class members (if they aren't removed
+    in the shrinking step).</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-keepclasseswithmembernames</b></code>
+    <i>class_specification</i></td>
+<td>Preserve the names of the specified classes <i>and</i> class members, if
+    all of the specified class members are present (after the shrinking
+    step).</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-printseeds</b></code> [<i>filename</i>]</td>
+<td>List classes and class members matched by the various <code>-keep</code>
+    options, to the standard output or to the given file.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-dontshrink</b></code></td>
+<td>Don't shrink the input class files.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-printusage</b></code> [<i>filename</i>]</td>
+<td>List dead code of the input class files, to the standard output or to the
+    given file.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-dontoptimize</b></code></td>
+<td>Don't optimize the input class files.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-assumenosideeffects</b></code>
+    <i>class_specification</i></td>
+<td>Assume that the specified methods don't have any side effects, while
+    optimizing.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-allowaccessmodification</b></code></td>
+<td>Allow the access modifiers of classes and class members to be modified,
+    while optimizing.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-dontobfuscate</b></code></td>
+<td>Don't obfuscate the input class files.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-printmapping</b></code> [<i>filename</i>]</td>
+<td>Print the mapping from old names to new names for classes and class members
+    that have been renamed, to the standard output or to the given file.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-applymapping</b></code> <i>filename</i></td>
+<td>Reuse the given mapping, for incremental obfuscation.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-obfuscationdictionary</b></code>
+    <i>filename</i></td>
+<td>Use the words in the given text file as obfuscated method names.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-overloadaggressively</b></code></td>
+<td>Apply aggressive overloading while obfuscating.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-defaultpackage</b></code> [<i>package_name</i>]</td>
+<td>Repackage all class files that are renamed into the single given
+    package.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-dontusemixedcaseclassnames</b></code></td>
+<td>Don't generate mixed-case class names while obfuscating.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-keepattributes</b></code>
+    [<i>attribute_name<b>,</b>...</i>]</td>
+<td>Preserve the given optional attributes; typically
+    <code>LineNumberTable</code>, <code>LocalVariableTable</code>,
+    <code>SourceFile</code>, <code>Deprecated</code>, <code>Synthetic</code>,
+    <code>Signature</code>, and <code>InnerClasses</code>.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-renamesourcefileattribute</b></code>
+    [<i>string</i>]</td>
+<td>Put the given constant string in the <code>SourceFile</code>
+    attributes.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-verbose</b></code></td>
+<td>Write out some more information during processing.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-dontnote</b></code></td>
+<td>Don't print notes about class casts of variable dynamically created
+    objects.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-dontwarn</b></code></td>
+<td>Don't warn about unresolved references at all.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-ignorewarnings</b></code></td>
+<td>Print warnings about unresolved references, but continue processing
+    anyhow.</td>
+</tr>
+
+<tr>
+<td valign="top"><code><b>-dump</b></code> [<i>filename</i>]</td>
+<td>Write out the internal structure of the processed class files, to the
+    standard output or to the given file.</td>
+</tr>
+
+</table>
+<p>
+Notes:
+<ul>
+
+<li><i>class_path</i> is a list of jars, wars, ears, zips, and directories,
+    with optional filters, separated by path separators.
+<li><i>filename</i> can contain Java system properties delimited by
+    '<b><</b>' and '<b>></b>'.
+<li>If <i>filename</i> contains special characters, the entire name
+    should be quoted with single or double quotes.
+</ul>
+<p>
+
+<h2>Overview of <code>Keep</code> Options</h2>
+
+<table cellpadding="5">
+
+<tr>
+<th>Keep</th>
+<td>From being removed or renamed</td>
+<td>From being renamed</td>
+</tr>
+
+<tr>
+<td>Classes and class members</td>
+<td bgcolor="#E0E0E0"><code>-keep</code></td>
+<td bgcolor="#E0E0E0"><code>-keepnames</code></td>
+</tr>
+
+<tr>
+<td>Class members only</td>
+<td bgcolor="#E0E0E0"><code>-keepclassmembers</code></td>
+<td bgcolor="#E0E0E0"><code>-keepclassmembernames</code></td>
+</tr>
+
+<tr>
+<td>Classes and class members, if class members present</td>
+<td bgcolor="#E0E0E0"><code>-keepclasseswithmembers</code></td>
+<td bgcolor="#E0E0E0"><code>-keepclasseswithmembernames</code></td>
+</tr>
+
+</table>
+<p>
+
+<h2>Class Specifications</h2>
+
+<pre>
+[[<b>!</b>]<b>public</b>|<b>final</b>|<b>abstract</b> ...] ([<b>!</b>]<b>interface</b>)|<b>class</b> <i>classname</i>
+    [<b>extends</b>|<b>implements</b> <i>classname</i>]
+[<b>{</b>
+    [[<b>!</b>]<b>public</b>|<b>private</b>|<b>protected</b>|<b>static</b>|<b>volatile</b>|<b>transient</b> ...] <b><fields></b> |
+                                                                      (<i>fieldtype fieldname</i>)<b>;</b>
+    [[<b>!</b>]<b>public</b>|<b>private</b>|<b>protected</b>|<b>static</b>|<b>synchronized</b>|<b>native</b>|<b>abstract</b>|<b>strictfp</b> ...] <b><methods></b> |
+                                                                                           <b><init>(</b><i>argumenttype,...</i><b>)</b> |
+                                                                                           <i>classname</i><b>(</b><i>argumenttype,...</i><b>)</b> |
+                                                                                           (<i>returntype methodname</i><b>(</b><i>argumenttype,...</i><b>)</b>)<b>;</b>
+    [[<b>!</b>]<b>public</b>|<b>private</b>|<b>protected</b>|<b>static</b> ... ] <b>*;</b>
+    ...
+<b>}</b>]
+</pre>
+<p>
+Notes:
+<ul>
+<li>Class names must always be fully qualified, i.e. including their package
+    names.
+<li>Types in <i>classname</i>, <i>returntype</i>, and <i>argumenttype</i> can
+    contain wildcards: '<code><b>?</b></code>' for a single character,
+    '<code><b>*</b></code>' for any number of characters (but not the package
+    separator), '<code><b>**</b></code>' for any number of (any)
+    characters, and  '<code><b>%</b></code>' for any primitive type.
+<li><i>fieldname</i> and <i>methodname</i> can contain wildcards as well:
+    '<code><b>?</b></code>' for a single character and '<code><b>*</b></code>'
+    for any number of characters.
+</ul>
+<p>
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
diff --git a/docs/manual/retrace/examples.html b/docs/manual/retrace/examples.html
new file mode 100644
index 0000000..75f7ae4
--- /dev/null
+++ b/docs/manual/retrace/examples.html
@@ -0,0 +1,336 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="../style.css">
+<title>ReTrace Examples</title>
+</head>
+<body>
+
+<h2>Examples</h2>
+
+Some typical example uses:
+<ol>
+<li><a href="#with">Restoring a stack trace with line numbers</a>
+<li><a href="#withverbose">Restoring a stack trace with line numbers (verbose)</a>
+<li><a href="#without">Restoring a stack trace without line numbers</a>
+</ol>
+
+<a name="with"> </a>
+<h3>Restoring a stack trace with line numbers</h3>
+
+Assume for instance ProGuard itself has been obfuscated using the following
+extra options:
+<pre>
+-printmapping proguard.map
+
+-renamesourcefileattribute ProGuard
+-keepattributes SourceFile,LineNumberTable
+</pre>
+<p>
+
+Now assume the processed application throws an exception, and we have saved the
+stack trace in <code>proguard.trace</code>, shown below. Of course, in real
+life ProGuard rarely throws exceptions, so this is a purposely generated
+exception. :)
+
+<pre>
+Exception in thread "main" java.lang.Error: Random exception
+        at pro.bY.a(ProGuard:576)
+        at pro.bO.a(ProGuard:431)
+        at pro.bj.a(ProGuard:145)
+        at pro.bY.a(ProGuard:522)
+        at pro.bj.a(ProGuard:129)
+        at pro.bN.a(ProGuard:125)
+        at pro.bY.a(ProGuard:251)
+        at pro.bY.a(ProGuard:229)
+        at pro.l.a(ProGuard:55)
+        at pro.bo.b(ProGuard:405)
+        at pro.ci.a(ProGuard:51)
+        at pro.bo.a(ProGuard:356)
+        at pro.be.a(ProGuard:109)
+        at pro.bo.a(ProGuard:356)
+        at pro.be.a(ProGuard:186)
+        at pro.bg.a(ProGuard:369)
+        at pro.bY.a(ProGuard:286)
+        at pro.bh.a(ProGuard:55)
+        at pro.bg.b(ProGuard:408)
+        at pro.bY.a(ProGuard:190)
+        at pro.bg.a(ProGuard:369)
+        at pro.M.a(ProGuard:110)
+        at pro.bY.a(ProGuard:449)
+        at pro.M.a(ProGuard:99)
+        at pro.bo.a(ProGuard:372)
+        at pro.bY.a(ProGuard:649)
+        at pro.bY.a(ProGuard:112)
+        at pro.P.a(ProGuard:66)
+        at pro.p.a(ProGuard:83)
+        at pro.bU.a(ProGuard:69)
+        at pro.bo.a(ProGuard:356)
+        at pro.J.a(ProGuard:149)
+        at pro.I.a(ProGuard:49)
+        at pro.J.a(ProGuard:105)
+        at pro.cf.c(ProGuard:370)
+        at pro.cf.a(ProGuard:317)
+        at pro.bc.a(ProGuard:55)
+        at proguard.ProGuard.a(ProGuard:363)
+        at proguard.ProGuard.c(ProGuard:187)
+        at proguard.ProGuard.b(ProGuard:385)
+        at proguard.ProGuard.main(ProGuard:429)
+</pre>
+<p>
+
+We can then use the following command to recover the stack trace:
+<pre>
+<b>java -jar retrace.jar proguard.map proguard.trace</b>
+</pre>
+<p>
+
+The output will look as follows:
+<pre>
+Exception in thread "main" java.lang.Error: Random exception
+        at proguard.shrink.UsageMarker.visitInstruction(ProGuard:576)
+        at proguard.classfile.instruction.GenericInstruction.accept(ProGuard:431)
+        at proguard.classfile.CodeAttrInfo.instructionsAccept(ProGuard:145)
+        at proguard.shrink.UsageMarker.visitCodeAttrInfo(ProGuard:522)
+        at proguard.classfile.CodeAttrInfo.accept(ProGuard:129)
+        at proguard.classfile.ProgramMemberInfo.attributesAccept(ProGuard:125)
+        at proguard.shrink.UsageMarker.visitMemberInfo(ProGuard:251)
+        at proguard.shrink.UsageMarker.visitProgramMethodInfo(ProGuard:229)
+        at proguard.classfile.ProgramMethodInfo.accept(ProGuard:55)
+        at proguard.classfile.ProgramClassFile.methodAccept(ProGuard:405)
+        at proguard.classfile.visitor.NamedMethodVisitor.visitProgramClassFile(ProGuard:51)
+        at proguard.classfile.ProgramClassFile.accept(ProGuard:356)
+        at proguard.classfile.visitor.ClassFileUpDownTraveler.visitProgramClassFile(ProGuard:109)
+        at proguard.classfile.ProgramClassFile.accept(ProGuard:356)
+        at proguard.classfile.visitor.ClassFileUpDownTraveler.visitLibraryClassFile(ProGuard:186)
+        at proguard.classfile.LibraryClassFile.accept(ProGuard:369)
+        at proguard.shrink.UsageMarker.visitLibraryMethodInfo(ProGuard:286)
+        at proguard.classfile.LibraryMethodInfo.accept(ProGuard:55)
+        at proguard.classfile.LibraryClassFile.methodsAccept(ProGuard:408)
+        at proguard.shrink.UsageMarker.visitLibraryClassFile(ProGuard:190)
+        at proguard.classfile.LibraryClassFile.accept(ProGuard:369)
+        at proguard.classfile.ClassCpInfo.referencedClassAccept(ProGuard:110)
+        at proguard.shrink.UsageMarker.visitClassCpInfo(ProGuard:449)
+        at proguard.classfile.ClassCpInfo.accept(ProGuard:99)
+        at proguard.classfile.ProgramClassFile.constantPoolEntryAccept(ProGuard:372)
+        at proguard.shrink.UsageMarker.markCpEntry(ProGuard:649)
+        at proguard.shrink.UsageMarker.visitProgramClassFile(ProGuard:112)
+        at proguard.classfile.visitor.VariableClassFileVisitor.visitProgramClassFile(ProGuard:66)
+        at proguard.classfile.visitor.MultiClassFileVisitor.visitProgramClassFile(ProGuard:83)
+        at proguard.classfile.visitor.FilteredClassFileVisitor.visitProgramClassFile(ProGuard:69)
+        at proguard.classfile.ProgramClassFile.accept(ProGuard:356)
+        at proguard.classfile.ClassPool.classFileAccept(ProGuard:149)
+        at proguard.classfile.visitor.NamedClassFileVisitor.visitClassPool(ProGuard:49)
+        at proguard.classfile.ClassPool.accept(ProGuard:105)
+        at proguard.KeepCommand.executeShrinkingPhase(ProGuard:370)
+        at proguard.KeepCommand.execute(ProGuard:317)
+        at proguard.CompoundCommand.execute(ProGuard:55)
+        at proguard.ProGuard.executeCommands(ProGuard:363)
+        at proguard.ProGuard.shrink(ProGuard:187)
+        at proguard.ProGuard.execute(ProGuard:385)
+        at proguard.ProGuard.main(ProGuard:429)
+</pre>
+
+<a name="withverbose"> </a>
+<h3>Restoring a stack trace with line numbers (verbose)</h3>
+
+In the previous example, we could also use the verbose flag:
+<pre>
+<b>java -jar retrace.jar -verbose proguard.map proguard.trace</b>
+</pre>
+<p>
+
+The output will then look as follows:
+<pre>
+Exception in thread "main" java.lang.Error: Random exception
+        at proguard.shrink.UsageMarker.void visitInstruction(proguard.classfile.ClassFile,proguard.classfile.instruction.Instruction)(ProGuard:576)
+        at proguard.classfile.instruction.GenericInstruction.void accept(proguard.classfile.ClassFile,proguard.classfile.instruction.InstructionVisitor)(ProGuard:431)
+        at proguard.classfile.CodeAttrInfo.void instructionsAccept(proguard.classfile.ClassFile,proguard.classfile.instruction.InstructionVisitor)(ProGuard:145)
+        at proguard.shrink.UsageMarker.void visitCodeAttrInfo(proguard.classfile.ClassFile,proguard.classfile.CodeAttrInfo)(ProGuard:522)
+        at proguard.classfile.CodeAttrInfo.void accept(proguard.classfile.ClassFile,proguard.classfile.visitor.AttrInfoVisitor)(ProGuard:129)
+        at proguard.classfile.ProgramMemberInfo.void attributesAccept(proguard.classfile.ProgramClassFile,proguard.classfile.visitor.AttrInfoVisitor)(ProGuard:125)
+        at proguard.shrink.UsageMarker.void visitMemberInfo(proguard.classfile.ProgramClassFile,proguard.classfile.ProgramMemberInfo)(ProGuard:251)
+        at proguard.shrink.UsageMarker.void visitProgramMethodInfo(proguard.classfile.ProgramClassFile,proguard.classfile.ProgramMethodInfo)(ProGuard:229)
+        at proguard.classfile.ProgramMethodInfo.void accept(proguard.classfile.ProgramClassFile,proguard.classfile.visitor.MemberInfoVisitor)(ProGuard:55)
+        at proguard.classfile.ProgramClassFile.void methodAccept(proguard.classfile.visitor.MemberInfoVisitor,java.lang.String,java.lang.String)(ProGuard:405)
+        at proguard.classfile.visitor.NamedMethodVisitor.void visitProgramClassFile(proguard.classfile.ProgramClassFile)(ProGuard:51)
+        at proguard.classfile.ProgramClassFile.void accept(proguard.classfile.visitor.ClassFileVisitor)(ProGuard:356)
+        at proguard.classfile.visitor.ClassFileUpDownTraveler.void visitProgramClassFile(proguard.classfile.ProgramClassFile)(ProGuard:109)
+        at proguard.classfile.ProgramClassFile.void accept(proguard.classfile.visitor.ClassFileVisitor)(ProGuard:356)
+        at proguard.classfile.visitor.ClassFileUpDownTraveler.void visitLibraryClassFile(proguard.classfile.LibraryClassFile)(ProGuard:186)
+        at proguard.classfile.LibraryClassFile.void accept(proguard.classfile.visitor.ClassFileVisitor)(ProGuard:369)
+        at proguard.shrink.UsageMarker.void visitLibraryMethodInfo(proguard.classfile.LibraryClassFile,proguard.classfile.LibraryMethodInfo)(ProGuard:286)
+        at proguard.classfile.LibraryMethodInfo.void accept(proguard.classfile.LibraryClassFile,proguard.classfile.visitor.MemberInfoVisitor)(ProGuard:55)
+        at proguard.classfile.LibraryClassFile.void methodsAccept(proguard.classfile.visitor.MemberInfoVisitor)(ProGuard:408)
+        at proguard.shrink.UsageMarker.void visitLibraryClassFile(proguard.classfile.LibraryClassFile)(ProGuard:190)
+        at proguard.classfile.LibraryClassFile.void accept(proguard.classfile.visitor.ClassFileVisitor)(ProGuard:369)
+        at proguard.classfile.ClassCpInfo.void referencedClassAccept(proguard.classfile.visitor.ClassFileVisitor)(ProGuard:110)
+        at proguard.shrink.UsageMarker.void visitClassCpInfo(proguard.classfile.ClassFile,proguard.classfile.ClassCpInfo)(ProGuard:449)
+        at proguard.classfile.ClassCpInfo.void accept(proguard.classfile.ClassFile,proguard.classfile.visitor.CpInfoVisitor)(ProGuard:99)
+        at proguard.classfile.ProgramClassFile.void constantPoolEntryAccept(proguard.classfile.visitor.CpInfoVisitor,int)(ProGuard:372)
+        at proguard.shrink.UsageMarker.void markCpEntry(proguard.classfile.ClassFile,int)(ProGuard:649)
+        at proguard.shrink.UsageMarker.void visitProgramClassFile(proguard.classfile.ProgramClassFile)(ProGuard:112)
+        at proguard.classfile.visitor.VariableClassFileVisitor.void visitProgramClassFile(proguard.classfile.ProgramClassFile)(ProGuard:66)
+        at proguard.classfile.visitor.MultiClassFileVisitor.void visitProgramClassFile(proguard.classfile.ProgramClassFile)(ProGuard:83)
+        at proguard.classfile.visitor.FilteredClassFileVisitor.void visitProgramClassFile(proguard.classfile.ProgramClassFile)(ProGuard:69)
+        at proguard.classfile.ProgramClassFile.void accept(proguard.classfile.visitor.ClassFileVisitor)(ProGuard:356)
+        at proguard.classfile.ClassPool.void classFileAccept(proguard.classfile.visitor.ClassFileVisitor,java.lang.String)(ProGuard:149)
+        at proguard.classfile.visitor.NamedClassFileVisitor.void visitClassPool(proguard.classfile.ClassPool)(ProGuard:49)
+        at proguard.classfile.ClassPool.void accept(proguard.classfile.visitor.ClassPoolVisitor)(ProGuard:105)
+        at proguard.KeepCommand.void executeShrinkingPhase(proguard.classfile.ClassPool,proguard.classfile.ClassPool)(ProGuard:370)
+        at proguard.KeepCommand.void execute(int,proguard.classfile.ClassPool,proguard.classfile.ClassPool)(ProGuard:317)
+        at proguard.CompoundCommand.void execute(int,proguard.classfile.ClassPool,proguard.classfile.ClassPool)(ProGuard:55)
+        at proguard.ProGuard.void executeCommands(int)(ProGuard:363)
+        at proguard.ProGuard.void shrink()(ProGuard:187)
+        at proguard.ProGuard.void execute(java.lang.String[])(ProGuard:385)
+        at proguard.ProGuard.void main(java.lang.String[])(ProGuard:429)
+</pre>
+
+
+<a name="without"> </a>
+<h3>Restoring a stack trace without line numbers</h3>
+
+Assume for instance ProGuard itself has been obfuscated using the following
+extra options, this time without preserving the line number tables:
+<pre>
+-printmapping proguard.map
+</pre>
+<p>
+
+A stack trace <code>proguard.trace</code> will then lack line number
+information:
+<pre>
+Exception in thread "main" java.lang.Error: Random exception
+        at pro.bY.a(Unknown Source)
+        at pro.bO.a(Unknown Source)
+        at pro.bj.a(Unknown Source)
+        at pro.bY.a(Unknown Source)
+        at pro.bj.a(Unknown Source)
+        at pro.bN.a(Unknown Source)
+        at pro.bY.a(Unknown Source)
+        at pro.bY.a(Unknown Source)
+        at pro.l.a(Unknown Source)
+        at pro.bo.b(Unknown Source)
+        at pro.ci.a(Unknown Source)
+        at pro.bo.a(Unknown Source)
+        at pro.be.a(Unknown Source)
+        at pro.bo.a(Unknown Source)
+        at pro.be.a(Unknown Source)
+        at pro.bg.a(Unknown Source)
+        at pro.bY.a(Unknown Source)
+        at pro.bh.a(Unknown Source)
+        at pro.bg.b(Unknown Source)
+        at pro.bY.a(Unknown Source)
+        at pro.bg.a(Unknown Source)
+        at pro.M.a(Unknown Source)
+        at pro.bY.a(Unknown Source)
+        at pro.M.a(Unknown Source)
+        at pro.bo.a(Unknown Source)
+        at pro.bY.a(Unknown Source)
+        at pro.bY.a(Unknown Source)
+        at pro.P.a(Unknown Source)
+        at pro.p.a(Unknown Source)
+        at pro.bU.a(Unknown Source)
+        at pro.bo.a(Unknown Source)
+        at pro.J.a(Unknown Source)
+        at pro.I.a(Unknown Source)
+        at pro.J.a(Unknown Source)
+        at pro.cf.c(Unknown Source)
+        at pro.cf.a(Unknown Source)
+        at pro.bc.a(Unknown Source)
+        at proguard.ProGuard.a(Unknown Source)
+        at proguard.ProGuard.c(Unknown Source)
+        at proguard.ProGuard.b(Unknown Source)
+        at proguard.ProGuard.main(Unknown Source)
+</pre>
+<p>
+
+We can still use the same command to recover the stack trace:
+<pre>
+<b>java -jar retrace.jar proguard.map proguard.trace</b>
+</pre>
+<p>
+
+The output will now give a list of alternative original method names for each
+ambiguous obfuscated method name:
+<pre>
+Exception in thread "main" java.lang.Error: Random exception
+        at proguard.shrink.UsageMarker.visitProgramClassFile(Unknown Source)
+                                       visitLibraryClassFile
+                                       visitProgramFieldInfo
+                                       visitProgramMethodInfo
+                                       visitMemberInfo
+                                       visitLibraryFieldInfo
+                                       visitLibraryMethodInfo
+                                       visitIntegerCpInfo
+                                       visitLongCpInfo
+                                       visitFloatCpInfo
+                                       visitDoubleCpInfo
+                                       visitStringCpInfo
+                                       visitUtf8CpInfo
+                                       visitFieldrefCpInfo
+                                       visitInterfaceMethodrefCpInfo
+                                       visitMethodrefCpInfo
+                                       visitClassCpInfo
+                                       visitNameAndTypeCpInfo
+                                       visitUnknownAttrInfo
+                                       visitInnerClassesAttrInfo
+                                       visitConstantValueAttrInfo
+                                       visitExceptionsAttrInfo
+                                       visitCodeAttrInfo
+                                       visitLineNumberTableAttrInfo
+                                       visitLocalVariableTableAttrInfo
+                                       visitSourceFileAttrInfo
+                                       visitDeprecatedAttrInfo
+                                       visitSyntheticAttrInfo
+                                       visitInstruction
+                                       visitCpInstruction
+                                       visitExceptionInfo
+                                       visitInnerClassesInfo
+                                       visitLocalVariableInfo
+                                       markCpEntry
+                                       markAsUnused
+                                       isUsed
+        at proguard.classfile.instruction.GenericInstruction.create(Unknown Source)
+                                                             isWide
+                                                             getLength
+                                                             accept
+        at proguard.classfile.CodeAttrInfo.getAttribute(Unknown Source)
+                                           getAttrInfoLength
+                                           readInfo
+                                           accept
+                                           instructionsAccept
+                                           exceptionsAccept
+        [...]
+        at proguard.KeepCommand.executeShrinkingPhase(Unknown Source)
+                                access$100
+        at proguard.KeepCommand.keepField(Unknown Source)
+                                ensureMultiClassFileVisitorForMembers
+                                execute
+                                executeObfuscationPhase
+                                access$002
+                                access$000
+                                access$102
+                                access$108
+        at proguard.CompoundCommand.addCommand(Unknown Source)
+                                    execute
+        at proguard.ProGuard.readCommands(Unknown Source)
+                             obfuscate
+                             executeCommands
+        at proguard.ProGuard.shrink(Unknown Source)
+        at proguard.ProGuard.check(Unknown Source)
+                             execute
+        at proguard.ProGuard.main(Unknown Source)
+</pre>
+<p>
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
+
diff --git a/docs/manual/retrace/index.html b/docs/manual/retrace/index.html
new file mode 100644
index 0000000..d69eaea
--- /dev/null
+++ b/docs/manual/retrace/index.html
@@ -0,0 +1,25 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="../style.css">
+<title>ReTrace Manual</title>
+</head>
+<body>
+
+<h2>ReTrace</h2>
+
+<ol>
+<li><a href="introduction.html">Introduction</a>
+<li><a href="usage.html">Usage</a>
+<li><a href="examples.html">Examples</a>
+</ol>
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
diff --git a/docs/manual/retrace/introduction.html b/docs/manual/retrace/introduction.html
new file mode 100644
index 0000000..e90e150
--- /dev/null
+++ b/docs/manual/retrace/introduction.html
@@ -0,0 +1,66 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="../style.css">
+<title>ReTrace Introduction</title>
+</head>
+<body>
+
+<h2>Introduction</h2>
+
+<b>ReTrace</b> is a companion tool for <b>ProGuard</b> that 'de-obfuscates'
+stack traces. When an obfuscated program throws an exception, the resulting
+stack trace typically isn't very informative. Class names and method names
+have been replaced by short meaningless strings. Source file names and line
+numbers are missing altogether. While this may be intentional, it can also be
+inconvenient when debugging problems.
+<p>
+
+<table class="diagram" align="center">
+
+<tr>
+<td rowspan="1" class="lightblock">Original code</td>
+<td             class="transparentblock">- <b>ProGuard</b> →</td>
+<td rowspan="1" class="lightblock">Obfuscated code</td>
+</tr>
+
+<tr>
+<td rowspan="3" class="transparentblock"></td>
+<td             class="transparentblock">↓</td>
+<td             class="transparentblock">↓</td>
+</tr>
+
+<tr>
+<td             class="whiteblock">Mapping file</td>
+<td             class="transparentblock">↓</td>
+</tr>
+
+<tr>
+<td             class="transparentblock">↓</td>
+<td             class="transparentblock">↓</td>
+</tr>
+
+<tr>
+<td             class="whiteblock">Readable stack trace</td>
+<td             class="transparentblock">← <b>ReTrace</b> -</td>
+<td             class="whiteblock">Obfuscated stack trace</td>
+</tr>
+
+</table>
+<p>
+ReTrace can read an obfuscated stack trace and restore it to what it would
+look like without obfuscation. The restoration is based on the mapping file
+that ProGuard can write out during obfuscation. The mapping file links the
+original class names and class member names to their obfuscated names.
+<p>
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
+
diff --git a/docs/manual/retrace/usage.html b/docs/manual/retrace/usage.html
new file mode 100644
index 0000000..239c39c
--- /dev/null
+++ b/docs/manual/retrace/usage.html
@@ -0,0 +1,81 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="../style.css">
+<title>ReTrace Usage</title>
+</head>
+<body>
+
+<h2>Usage</h2>
+
+To run ReTrace, just type:
+<p>
+<p class="code">
+<code><b>java -jar retrace.jar [-verbose] </b></code><i>mapping_file</i>
+[<i>stacktrace_file</i>]
+</p>
+where the arguments have the following meaning:
+<p>
+<table cellspacing="10">
+<tr>
+<td valign="top"><code><b>-verbose</b></code></td>
+
+<td>Optionally specifies to print out more informative stack traces that
+    include not only method names, but also method return types and
+    arguments.</td>
+
+</tr>
+<tr>
+<td valign="top"><i>mapping_file</i></td>
+
+<td>Specifies the name of the mapping file, produced by ProGuard with the
+    option "<code>-printmapping</code> <i>mapping_file</i>", while obfuscating
+    the application that produced the stack trace.</td>
+
+</tr>
+<tr>
+<td valign="top"><i>stacktrace_file</i></td>
+
+<td>Optionally specifies the name of the file containing the stack trace. If
+    no file is specified, a stack trace is read from the standard input. Blank
+    lines and unrecognized lines are ignored, as far as possible.</td>
+
+</tr>
+</table>
+
+The restored stack trace is printed to the standard output. The completeness
+of the restored stack trace depends on the presence of line number tables in
+the obfuscated class files:
+
+<ul>
+<li>If all line numbers have been preserved while obfuscating the application,
+    ReTrace will be able to restore the stack trace completely.
+    <p>
+
+<li>If the line numbers have been removed, mapping obfuscated method names
+    back to their original names has become ambiguous. Retrace will list all
+    possible original method names for each line in the stack trace. The user
+    can then try to deduce the actual stack trace manually, based on the logic
+    of the program.
+
+</ul>
+<p>
+
+Preserving line number tables is explained in detail in this <a
+href="../examples.html#stacktrace">example</a> in the ProGuard User Manual.
+<p>
+
+Unobfuscated elements and obfuscated elements for which no mapping is available
+will be left unchanged.
+<p>
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
+
diff --git a/docs/manual/sections.html b/docs/manual/sections.html
new file mode 100644
index 0000000..fab98e2
--- /dev/null
+++ b/docs/manual/sections.html
@@ -0,0 +1,136 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-script-type" content="text/javascript">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="../style.css">
+<title>Sections</title>
+
+<script type="text/javascript" language="JavaScript">
+<!--
+function defineSection(aName, aTarget, aURL) {
+  document.write("<tr><th class=\"sections\"");
+  document.write(document.body != null ?
+    (" onMouseOver=\"lightUp(this)\" onMouseOut=\"lightDown(this)\" onClick=\"goTo(this,'"+aTarget+"','"+aURL+"')\" onAfterUpdate=\"lightUp(this)\">"+aName) :
+    ("><a target=\""+aTarget+"\" href=\""+aURL+"\">"+aName+"</a>"));
+  document.write("</th></tr>");
+}
+
+function lightUp(aTH) {
+  if (aTH != fTH)
+    aTH.bgColor = "#eeeeee";
+}
+function lightDown(aTH) {
+  if (aTH != fTH)
+    aTH.bgColor = "";
+}
+function goTo(aTH, aTarget, aURL) {
+  if (fTH != null)
+    fTH.bgColor = "";
+
+  fTH = aTH;
+  fTH.bgColor = "white";
+  f = findFrameByName(parent.frames, aTarget);
+  if (f != null)
+    f.document.location.href=aURL;
+}
+function findFrameByName(aFrames, aFrameName) {
+  for (i = 0; i < aFrames.length; i++)
+    if (aFrames[i].name == aFrameName)
+      return aFrames[i];
+  return null;
+}
+//-->
+</script>
+
+</head>
+<body class="title">
+
+<script type="text/javascript" language="JavaScript">
+<!--
+var fTH = null;
+//-->
+</script>
+
+<script type="text/javascript" language="JavaScript">
+<!--
+document.write("<table class=\"sections\"");
+defineSection("<< Main Menu","sections","../sections.html");
+document.write("<tr><th class=\"sections\" bgcolor=\"white\">ProGuard<br>Manual</th></tr>");
+defineSection("Introduction",   "main","introduction.html");
+defineSection("Usage",          "main","usage.html");
+defineSection("Limitations",    "main","limitations.html");
+defineSection("Examples",       "main","examples.html");
+defineSection("Troubleshooting","main","troubleshooting.html");
+defineSection("Reference Card", "main","refcard.html");
+defineSection("GUI",            "main","gui.html");
+defineSection("Ant Task",       "main","ant.html");
+defineSection("J2ME WTK",       "main","wtk.html");
+document.write("<tr><th class=\"sections\" bgcolor=\"white\">ReTrace<br>Manual</th></tr>");
+defineSection("Introduction",   "main","retrace/introduction.html");
+defineSection("Usage",          "main","retrace/usage.html");
+defineSection("Examples",       "main","retrace/examples.html");
+document.write("</table>");
+document.write("<p>");
+document.write("<center>");
+document.write("<small>With support of</small>");
+document.write("<p>");
+document.write("<a href=\"http://sourceforge.net/projects/proguard/\" target=\"other\">");
+document.write("<img src=\"");
+document.write(document.location.hostname == "proguard.sourceforge.net" ?
+  "http://sourceforge.net/sflogo.php?group_id=54750&type=1" :
+  "../sflogo.png");
+document.write("\" width=\"88\" height=\"31\" border=\"0\" alt=\"SourceForge\">");
+document.write("</a>");
+document.write("<p>");
+document.write("<a href=\"http://www.luciad.com/\" target=\"other\">");
+document.write("<img src=\"../luciadlogo.png\" width=\"88\" height=\"24\" border=\"0\" alt=\"Luciad\">");
+document.write("</a>");
+document.write("</center>");
+//-->
+</script>
+
+<noscript>
+<table
+ width="120"
+ border="1"
+ style="border-style:outset"
+ cellspacing="0"
+ cellpadding="0"
+ background="../steel.gif"
+ bgcolor="#cccccc">
+
+<tr><th height="40" valign="middle"><a href="../sections.html"><< Main menu</a></th></tr>
+
+<tr><th class="sections" bgcolor="white">ProGuard<br>Manual</th></tr>
+<tr><th class="sections"><a target="main" href="introduction.html">Introduction</a></th></tr>
+<tr><th class="sections"><a target="main" href="usage.html">Usage</a></th></tr>
+<tr><th class="sections"><a target="main" href="limitations.html">Limitations</a></th></tr>
+<tr><th class="sections"><a target="main" href="examples.html">Examples</a></th></tr>
+<tr><th class="sections"><a target="main" href="troubleshooting.html">Troubleshooting</a></th></tr>
+<tr><th class="sections"><a target="main" href="refcard.html">Reference Card</a></th></tr>
+<tr><th class="sections"><a target="main" href="gui.html">GUI</a></th></tr>
+<tr><th class="sections"><a target="main" href="ant.html">Ant Task</a></th></tr>
+<tr><th class="sections"><a target="main" href="wtk.html">J2ME WTK</a></th></tr>
+
+<tr><th class="sections" bgcolor="white">ReTrace<br>Manual</th></tr>
+<tr><th class="sections"><a target="main" href="retrace/introduction.html">Introduction</a></th></tr>
+<tr><th class="sections"><a target="main" href="retrace/usage.html">Usage</a></th></tr>
+<tr><th class="sections"><a target="main" href="retrace/examples.html">Examples</a></th></tr>
+
+</table>
+<p>
+<center>
+<small>With support of</small>
+<p>
+<a href="http://sourceforge.net/projects/proguard/" target="other">
+<img src="../sflogo.png" width="88" height="31" alt="SourceForge"></a>
+<p>
+<a href="http://www.luciad.com/" target="other">
+<img src="../luciadlogo.png" width="88" height="24" alt="Luciad"></a>
+</center>
+</noscript>
+
+</body>
+</html>
diff --git a/docs/manual/style.css b/docs/manual/style.css
new file mode 100644
index 0000000..1b0cc55
--- /dev/null
+++ b/docs/manual/style.css
@@ -0,0 +1,81 @@
+ at charset "iso-8859-1";
+
+/* Global settings. */
+
+body {
+  background: #FFFFFF;
+}
+
+h1 {
+  text-align: center;
+}
+
+h2 {
+  background: #EEEEFF;
+  padding: 10px;
+}
+
+dt {
+  padding: 6px;
+}
+
+dd {
+  padding: 6px;
+}
+
+pre {
+  padding: 10px;
+  background: #E0E0E0;
+}
+
+/* Settings for variable width code. */
+
+p.code {
+  padding: 10px;
+  background: #E0E0E0;
+}
+
+
+/* Settings for diagrams. */
+
+table.diagram {
+  padding: 8px;
+  border: none;
+  border-spacing: 2px;
+}
+
+td.transparentblock {
+  text-align: center;
+  padding: 10px 0px;
+}
+
+td.whiteblock {
+  width: 100px;
+  text-align: center;
+  border: solid #C0C0C0 1px;
+  background: #E0E0E0;
+  padding: 10px 0px;
+}
+
+td.lightblock {
+  width: 100px;
+  text-align: center;
+  border: solid #8888FF 1px;
+  background: #BBBBFF;
+  padding: 20px 0px;
+}
+
+td.darkblock {
+  width: 100px;
+  text-align: center;
+  background: #8888FF;
+  padding: 20px 0px;
+}
+
+/* Settings for buttons. */
+
+td.button {
+  background: #E0E0E0;
+  border: outset #FFFFFF 1px;
+  font-weight: bold;
+}
diff --git a/docs/manual/troubleshooting.html b/docs/manual/troubleshooting.html
new file mode 100644
index 0000000..b598108
--- /dev/null
+++ b/docs/manual/troubleshooting.html
@@ -0,0 +1,269 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard Troubleshooting</title>
+</head>
+<body>
+
+<h2>Troubleshooting</h2>
+
+While preparing a configuration for processing your code, you may bump into a
+few problems. The following sections discuss some common issues and solutions:
+<ul>
+<li><a href="#processing">Problems while processing</a>
+<li><a href="#preverifying">Problems while preverifying for J2ME</a>
+<li><a href="#runtime">Problems at run-time</a>
+</ul>
+
+<a name="processing"> </a>
+<h2>Problems while processing</h2>
+
+ProGuard may print out some notes and non-fatal warnings:
+
+<dl>
+<dt><a name="dynamical"><b>Note: ... calls '(...)Class.forName(variable).newInstance()'</b></a></dt>
+
+<dd>ProGuard lists all class casts of dynamically created class instances,
+    like "<code>(MyClass)Class.forName(variable).newInstance()</code>".
+    Depending on your application, you may need to keep the mentioned classes
+    with an option like "<code>-keep class MyClass</code>", or their
+    implementations with an option like "<code>-keep class * implements
+    MyClass</code>". You can switch off these notes by specifying the
+    <code>-dontnote</code> option.</dd>
+
+<dt><a name="duplicateclass"><b>Note: duplicate definition of program/library class</b></a></dt>
+
+<dd>Your program jars or library jars contain multiple definitions of the
+    listed classes. ProGuard continues processing as usual, only considering
+    the first definitions. The warning may be an indication of some problem
+    though, so it's advisable to remove the duplicates. A convenient way to do
+    so is by specifying filters on the input jars or library jars. You can
+    switch off these notes by specifying the <code>-dontnote</code>
+    option.</dd>
+
+<dt><a name="duplicatezipentry"><b>Warning: can't write resource... Duplicate zip entry</b></a></dt>
+
+<dd>Your input jars contain multiple resource files with the same name.
+    ProGuard continues copying the resource files as usual, skipping any files
+    with previously used names. Once more, the warning may be an indication of
+    some problem though, so it's advisable to remove the duplicates. A
+    convenient way to do so is by specifying filters on the input jars. There
+    is no option to switch off these warnings.</dd>
+
+</dl>
+<p>
+
+ProGuard may terminate when it encounters parsing errors or I/O errors, or
+some more serious warnings:
+
+<dl>
+<dt><a name="unresolvedclass"><b>Warning: can't find superclass or interface</b></a></dt>
+
+<dd>If there are unresolved references to superclasses or interfaces, you most
+    likely forgot to specify an essential library. All libraries that are
+    referenced by your code should be specified, including the Java run-time
+    library. For specifying libraries, use the <code>-libraryjars</code>
+    option.
+    <p>
+    If the class that is reported as missing is a non-public library class,
+    you should specify the <code>-dontskipnonpubliclibraryclasses</code>
+    option. A common example is the class
+    <code>java.util.zip.ZipConstants</code>, which is used as an interface
+    class in some public classes, even though it is only package visible (in
+    this case, the warning could also be ignored, because the class is not a
+    fundamental part of the class hierarchy).
+    <p>
+    If you're missing a library and you're absolutely sure it isn't used
+    anyway, you can try your luck with the <code>-ignorewarnings</code>
+    option, or even the <code>-dontwarn</code> option.</dd>
+
+<dt><a name="unresolvedclassmember"><b>Warning: can't find referenced field/method</b></a></dt>
+
+<dd>If there are unresolved references to class members in input classes, your
+    class files are most likely inconsistent. Possibly, some class file didn't
+    get recompiled properly, or some class file was left behind after its
+    source file was removed. Try removing all class files, recompiling them,
+    zipping them up, and running ProGuard again.
+    <p>
+    If your program classes reside in the same packages as library classes,
+    and refer to their package visible class members, then you should specify
+    the <code>-dontskipnonpubliclibraryclassmembers</code> option.</dd>
+
+<dt><a name="keep"><b>Error: You have to specify '-keep' options</b></a></dt>
+
+<dd>You either forgot to specify <code>-keep</code> options, or you mistyped
+    the class names. ProGuard has to know exactly what you want to keep: an
+    application, an applet, a servlet, a midlet,..., or any combination of
+    these. Without the proper seed specifications, ProGuard would shrink,
+    optimize, or obfuscate all class files away.</dd>
+
+</dl>
+<p>
+
+Should ProGuard crash while processing your application:
+
+<dl>
+<dt><a name="outofmemoryerror"><b>OutOfMemoryError</b></a></dt>
+
+<dd>You can try increasing the heap size of the Java virtual machine (with the
+    usual <code>-Xms</code> and <code>-Xmx</code> options). You can also
+    reduce the amount of memory that ProGuard needs by removing unnecessary
+    library jars from your configuration, or by filtering out unused library
+    packages and classes. Remember that only classes or interfaces that are
+    extended or implemented by classes in your input jars are required.</dd>
+
+<dt><a name="otherwise"><b>Otherwise...</b></a></dt>
+
+<dd>Maybe your class files are corrupt. See if recompiling them and trying
+    again helps. If not, please report the problem, preferably with the
+    simplest example that causes ProGuard to crash.</dd>
+
+</dl>
+<p>
+
+<a name="preverifying"> </a>
+<h2>Problems while preverifying for J2ME</h2>
+
+If ProGuard seems to run fine, but the preverifier subsequently produces
+errors, it's usually for a single reason:
+<dl>
+
+<dt><a name="invalidclassexception1"><b>InvalidClassException</b>,
+    <b>class loading error</b>, or
+    <b>verification error</b></a></dt>
+
+<dd>If you get any such message from the preverifier, you are probably working
+    on a platform with a case-insensitive file system, such as Windows. The
+    <code>preverify</code> tool always unpacks the jars, so class files with
+    similar lower-case and upper-case names overwrite each other. You can use
+    ProGuard's <code>-dontusemixedcaseclassnames</code> option to work around
+    this problem.</dd>
+
+</dl>
+<p>
+
+<a name="runtime"> </a>
+<h2>Problems at run-time</h2>
+
+If ProGuard runs fine, but your processed application doesn't work, there
+might be several reasons:
+
+<dl>
+<dt><a name="disappearingclasses"><b>Disappearing classes</b></a></dt>
+
+<dd>If you are working on Windows and it looks like some classes have
+    disappeared from your output, you should make sure you're not writing
+    your output class files to a directory (or unpacking the output jar). On
+    platforms with case-insensitive file systems, such as Windows, unpacking
+    tools often let class files with similar lower-case and upper-case names
+    overwrite each other. If you really can't switch to a different operating
+    system, you could consider using ProGuard's
+    <code>-dontusemixedcaseclassnames</code> option.</dd>
+
+<dt><a name="notkept"><b>Classes or class members not being kept</b></a></dt>
+
+<dd>If ProGuard is not keeping the right classes or class members, make
+    sure you are using fully qualified class names. If the package name of
+    some class is missing, ProGuard won't match the elements that you might be
+    expecting. It may help to double-check for typos too. You can use the
+    <code>-printseeds</code> option to see which elements are being kept
+    exactly.</dd>
+
+<dt><a name="stacktraces"><b>Stack traces without class names or line numbers</b></a></dt>
+
+<dd>If your stack traces don't contain any class names or lines numbers,
+    even though you are keeping the proper attributes, make sure this debugging
+    information is present in your compiled code to start with. Notably the Ant
+    javac task has debugging information switched off by default.</dd>
+
+<dt><a name="noclassdeffounderror"><b>NoClassDefFoundError</b></a></dt>
+
+<dd>Your class path is probably incorrect. It should at least contain all
+    library jars and, of course, your processed program jar.</dd>
+
+<dt><a name="classnotfoundexception"><b>ClassNotFoundException</b></a></dt>
+
+<dd>Your code is probably calling <code>Class.forName</code>, trying to create
+    the missing class dynamically. ProGuard can only detect constant name
+    arguments, like <code>Class.forName("mypackage.MyClass")</code>. For
+    variable name arguments like <code>Class.forName(someClass)</code>, you
+    have to keep all possible classes using the <code>-keep</code> option,
+    e.g. "<code>-keep class mypackage.MyClass</code>" or "<code>-keep class *
+    implements mypackage.MyInterface</code>".</dd>
+
+<dt><a name="nosuchmethodexception"><b>NoSuchMethodException</b></a></dt>
+
+<dd>Your code is probably calling something like
+    <code>myClass.getMethod</code>, trying to find some method dynamically.
+    Since ProGuard isn't detecting this (yet), you have to keep the missing
+    method in using the <code>-keep</code> option, e.g. "<code>-keep class
+    mypackage.MyClass { void myMethod(); }</code>".</dd>
+
+<dt><a name="nullpointerexception"><b>NullPointerException</b> (or missing resources, icons, sounds,...)</a></dt>
+
+<dd>Your processed code may be unable to find some resource files. ProGuard
+    currently simply copies resource files over from the input jars to the
+    output jar. Their names and contents remain unchanged. If you've used the
+    <code>-defaultpackage</code> option, the package names of some classes may
+    have changed, and along with them, the directory in which they look for
+    their resource files. It's better not to use this option in these
+    circumstances.</dd>
+
+<dt><a name="invalidclassexception2"><b>InvalidClassException</b>,
+    <b>class loading error</b>, or
+    <b>verification error</b> (in J2ME)</a></dt>
+
+<dd>If you get such an error in J2ME, you may have
+    forgotten to preverify your program jar <i>after</i> having processed it
+    with ProGuard.</dd>
+
+<dt><a name="securityexception"><b>SecurityException: SHA1 digest error</b></a></dt>
+
+<dd>You may have forgotten to sign your program jar <i>after</i> having
+    processed it with ProGuard.</dd>
+
+<dt><a name="nosuchfieldormethod"><b>Error: No Such Field or Method</b>,
+    <b>Error verifying method</b> (in J2ME emulator)</a></dt>
+
+<dd>If you get such a message in a Motorola or Sony Ericsson J2ME phone
+    emulator, it's because these emulators don't like packageless classes
+    and/or overloaded fields and methods. You can work around it by not using
+    the options <b>-defaultpackage ''</b> and <b>-overloadaggressively</b>. If
+    you're using the J2ME WTK plugin, you can adapt the configuration
+    <code>proguard/wtk/default.pro</code> that's inside the
+    <code>proguard.jar</code>.</dd>
+
+<dt><a name="classformaterror"><b>ClassFormatError: repetitive field name/signature</b></a></dt>
+
+<dd>You are probably processing some code that has been obfuscated before with
+    the <code>-overloadaggressively</code> option. You should then use the
+    same option again in the second processing round.</dd>
+
+<dt><a name="nosuchmethoderror"><b>NoSuchMethodError</b> or
+    <b>AbstractMethodError</b></a></dt>
+
+<dd>Again, you should make sure you're not writing your output class files to a
+    directory on a platform with a case-insensitive file system, such as
+    Windows. Please refer to the first item on this list for details.
+    <p>
+    Furthermore, you should check whether you have specified your program jars
+    and library jars properly. Program classes can refer to library classes,
+    but not the other way around.
+    <p>
+    If all of this seems ok, perhaps there's a bug in ProGuard (gasp!). If so,
+    please report it, preferably with the simplest example on which you can
+    find ProGuard to fail.</dd>
+
+</dl>
+<p>
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
diff --git a/docs/manual/usage.html b/docs/manual/usage.html
new file mode 100644
index 0000000..6b96def
--- /dev/null
+++ b/docs/manual/usage.html
@@ -0,0 +1,849 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard Usage</title>
+</head>
+<body>
+
+<h2>Usage</h2>
+
+To run ProGuard, just type:
+<p class="code">
+<code><b>java -jar proguard.jar </b></code><i>options</i> ...
+</p>
+Options can also be put in one or more configuration files. Typically, you'll
+put most options in a configuration file (say, <code>myconfig.pro</code>), and
+just call:
+<p class="code">
+<code><b>java -jar proguard.jar @myconfig.pro</b></code>
+</p>
+You can simply combine command line options and options from configuration
+files, e.g.:
+<p class="code">
+<code><b>java -jar proguard.jar @myconfig.pro -verbose</b></code>
+</p>
+<p>
+In a configuration file, a <code><b>#</b></code> sign and all remaining
+characters on that line are ignored, allowing you to add comments.
+<p>
+Extra whitespace between words and delimiters is ignored. To specify file
+names with spaces or special characters, words can be quoted with single or
+double quotes. Note that the quotes may need to be escaped when used on the
+command line, to avoid them being gobbled by the shell.
+<p>
+Options can be grouped arbitrarily in arguments on the command line and in
+lines in configuration files. This means that you can quote any arbitrary
+section of command line options, to avoid shell expansion of special
+characters, for instance.
+<p>
+The order of the options is generally irrelevant. They can be abbreviated to
+their first unique characters.
+<p>
+
+The sections below provide more details:
+<ul>
+<li><a href="#iooptions">Input/Output Options</a>
+<li><a href="#keepoptions">Keep Options</a>
+<li><a href="#shrinkingoptions">Shrinking Options</a>
+<li><a href="#optimizationoptions">Optimization Options</a>
+<li><a href="#obfuscationoptions">Obfuscation Options</a>
+<li><a href="#classpath">Class Paths</a>
+<li><a href="#filename">File Names</a>
+<li><a href="#filters">Filters</a>
+<li><a href="#keepoverview">Overview of <code>Keep</code> Options</a>
+<li><a href="#classspecification">Class Specifications</a>
+</ul>
+
+<a name="iooptions"> </a>
+<h2>Input/Output Options</h2>
+
+<dl>
+<dt><code><b>@</b></code><a href="#filename"><i>filename</i></a></dt>
+
+<dd>Short for '<code>-include</code> <i>filename</i>'.</dd>
+
+<dt><a name="include"><code><b>-include</b></code></a>
+    <a href="#filename"><i>filename</i></a></dt>
+
+<dd>Recursively reads configuration options from the given file
+    <i>filename</i>.</dd>
+
+<dt><a name="injars"><code><b>-injars</b></code></a>
+    <a href="#classpath"><i>class_path</i></a></dt>
+
+<dd>Specifies the input jars (or wars, ears, zips, or directories) of the
+    application to be processed. The class files in these jars will be
+    processed and written to the output jars. Any non-class files will be
+    copied without changes. Please be aware of any temporary files (e.g.
+    created by IDEs), especially if you are reading your input files straight
+    from directories. The entries in the class path can be filtered, as
+    explained in the <a href="#filters">filters</a> section. For better
+    readability, class path entries can be specified using multiple
+    <code>-injars</code> options.</dd>
+
+<dt><a name="outjars"><code><b>-outjars</b></code></a>
+    <a href="#classpath"><i>class_path</i></a></dt>
+
+<dd>Specifies the names of the output jars (or wars, ears, zips, or
+    directories). The processed input of the preceding <code>-injars</code>
+    options will be written to the named jars. This allows you to collect the
+    contents of groups of input jars into corresponding groups of output jars.
+    In addition, the output entries can be filtered, as explained in the <a
+    href="#filters">filters</a> section. Each processed class file or resource
+    file is then written to the first output entry with a matching filter,
+    within the group of output jars.
+    <p>
+    You must avoid letting the output files overwrite any input files. For
+    better readability, class path entries can be specified using multiple
+    <code>-outjars</code> options. Without any <code>-outjars</code> options,
+    no jars will be written.</dd>
+
+<dt><a name="libraryjars"><code><b>-libraryjars</b></code></a>
+    <a href="#classpath"><i>class_path</i></a></dt>
+
+<dd>Specifies the library jars (or wars, ears, zips, or directories) of the
+    application to be processed. The files in these jars will not be included
+    in the output jars. The specified library jars should at least contain the
+    class files that are <i>extended</i> by application class files. Library
+    class files that are only <i>called</i> needn't be present, although their
+    presence can improve the results of the optimization step. The entries in
+    the class path can be filtered, as explained in the <a
+    href="#filters">filters</a> section. For better readability, class path
+    entries can be specified using multiple <code>-libraryjars</code> options.
+    <p>
+    Please note that the boot path and the class path set for running ProGuard
+    are not considered when looking for library classes. This means that you
+    explicitly have to specify the run-time jar that your code will use.
+    Although this may seem cumbersome, it allows you to process applications
+    targeted at different run-time environments. For example, you can process
+    <a href="examples.html#application">J2SE applications</a> as well as <a
+    href="examples.html#midlet">J2ME midlets</a>, just by specifying the
+    appropriate run-time jar.</dd>
+
+<dt><a name="dontskipnonpubliclibraryclasses"><code><b>-dontskipnonpubliclibraryclasses</b></code></a></dt>
+
+<dd>Specifies not to ignore non-public library classes. By default, non-public
+    library classes are skipped while parsing library jars. The classes are
+    typically not relevant during processing, since they don't affect the
+    actual program code in the input jars. Ignoring them reduces memory usage
+    and processing time. Occasionally, a badly designed library may contain a
+    non-public library class that is extended/implemented by a public library
+    class. If the latter library class in turn is extended/implemented by a
+    program class, ProGuard will complain that it can't find the non-public
+    library class, which it had ignored during parsing. This option will
+    overcome that problem, at the cost of greater memory usage and longer
+    processing time.</dd>
+
+<dt><a name="dontskipnonpubliclibraryclassmembers"><code><b>-dontskipnonpubliclibraryclassmembers</b></code></a></dt>
+
+<dd>Specifies not to ignore package visible library class members (fields and
+    methods). By default, these class members are skipped while parsing
+    library classes, as program classes will generally not refer to them.
+    Sometimes however, program classes reside in the same packages as library
+    classes, and they do refer to their package visible class members. In
+    those cases, it can be useful to actually read the class members, in order
+    to make sure the processed code remains consistent.</dd>
+
+</dl>
+<p>
+
+<a name="keepoptions"> </a>
+<h2>Keep Options</h2>
+
+<dl>
+<dt><a name="keep"><code><b>-keep</b></code></a>
+    <a href="#classspecification"><i>class_specification</i></a></dt>
+
+<dd>Specifies classes and class members (fields and methods) to be preserved.
+    They will be preserved with their original names. This is typically the
+    required seed for recursively determining which other classes and class
+    members need to be preserved. For example, in order to <a
+    href="examples.html#application">keep an application</a>, you can specify
+    the main class along with its main method. In order to <a
+    href="examples.html#library">process a library</a>, you should specify all
+    publicly accessible items.</dd>
+
+<dt><a name="keepclassmembers"><code><b>-keepclassmembers</b></code></a>
+    <a href="#classspecification"><i>class_specification</i></a></dt>
+
+<dd>Specifies class members to be preserved, if their classes are preserved as
+    well. For example, you may want to <a
+    href="examples.html#serializable">keep all serialization fields and
+    methods</a> of classes that implement the <code>Serializable</code>
+    interface.</dd>
+
+<dt><a name="keepclasseswithmembers"><code><b>-keepclasseswithmembers</b></code></a>
+    <a href="#classspecification"><i>class_specification</i></a></dt>
+
+<dd>Specifies classes and class members to be preserved, on the condition that
+    all of the specified class members are present. For example, you may want
+    to <a href="examples.html#applications">keep all applications</a> that
+    have a main method, without having to list them explicitly.</dd>
+
+<dt><a name="keepnames"><code><b>-keepnames</b></code></a>
+    <a href="#classspecification"><i>class_specification</i></a></dt>
+
+<dd>Specifies classes and class members whose names are to be preserved, if
+    they aren't removed in the shrinking phase. For example, you may want to
+    <a href="examples.html#serializable">keep all class names</a> of classes
+    that implement the <code>Serializable</code> interface, so that the
+    processed code remains compatible with any originally serialized classes.
+    Classes that aren't used at all can still be removed. Only applicable when
+    obfuscating.</dd>
+
+<dt><a name="keepclassmembernames"><code><b>-keepclassmembernames</b></code></a>
+    <a href="#classspecification"><i>class_specification</i></a></dt>
+
+<dd>Specifies class members whose names are to be preserved, if they aren't
+    removed in the shrinking phase. For example, you may want to preserve the
+    name of the synthetic <code>class$</code> methods when <a
+    href="examples.html#library">processing a library</a>, so obfuscators can
+    detect it again when processing an application that uses the processed
+    library (although ProGuard itself doesn't need this). Only applicable when
+    obfuscating.</dd>
+
+<dt><a name="keepclasseswithmembernames"><code><b>-keepclasseswithmembernames</b></code></a>
+    <a href="#classspecification"><i>class_specification</i></a></dt>
+
+<dd>Specifies classes and class members whose names are to be preserved, on
+    the condition that all of the specified class members are present after
+    the shrinking phase. For example, you may want to <a
+    href="examples.html#native">keep all native method names</a> and the names
+    of their classes, so that the processed code can still link with the
+    native library code. Native methods that aren't used at all can still be
+    removed. If a class file is used, but none of its native methods are, its
+    name will still be obfuscated. Only applicable when obfuscating.</dd>
+
+<dt><a name="printseeds"><code><b>-printseeds</b></code></a>
+    [<a href="#filename"><i>filename</i></a>]</dt>
+
+<dd>Specifies to exhaustively list classes and class members matched by the
+    various <code>-keep</code> options. The list is printed to the standard
+    output or to the given file. The list can be useful to verify if the
+    intended class members are really found, especially if you're using
+    wildcards. For example, you may want to list all the <a
+    href="examples.html#applications">applications</a> or all the <a
+    href="examples.html#applets">applets</a> that you are keeping.</dd>
+
+</dl>
+<p>
+
+<a name="shrinkingoptions"> </a>
+<h2>Shrinking Options</h2>
+
+<dl>
+<dt><a name="dontshrink"><code><b>-dontshrink</b></code></a></dt>
+
+<dd>Specifies not to shrink the input class files. By default, shrinking is
+    applied; all classes and class members are removed, except for the ones
+    listed by the various <code>-keep</code> options, and the ones on which
+    they depend, directly or indirectly.</dd>
+
+<dt><a name="printusage"><code><b>-printusage</b></code></a>
+    [<a href="#filename"><i>filename</i></a>]</dt>
+
+<dd>Specifies to list dead code of the input class files. The list is printed
+    to the standard output or to the given file. For example, you can <a
+    href="examples.html#deadcode">list the unused code of an application</a>.
+    Only applicable when shrinking.</dd>
+
+</dl>
+<p>
+
+<a name="optimizationoptions"> </a>
+<h2>Optimization Options</h2>
+
+<dl>
+<dt><a name="dontoptimize"><code><b>-dontoptimize</b></code></a></dt>
+
+<dd>Specifies not to optimize the input class files. By default, optimization
+    is enabled; all methods are optimized at a bytecode level.</dd>
+
+<dt><a name="assumenosideeffects"><code><b>-assumenosideeffects</b></code></a>
+    <a href="#classspecification"><i>class_specification</i></a></dt>
+
+<dd>Specifies methods that don't have any side effects (other than maybe
+    returning a value). In the optimization step, ProGuard will then remove
+    calls to such methods, if it can determine that the return values aren't
+    used. Note that ProGuard will analyze your program code to find such
+    methods automatically. It will not analyze library code, for which this
+    option can thus be useful. For example, you could specify the method
+    <code>System.currentTimeMillis()</code>, so that any idle calls to it will
+    be removed. Only applicable when optimizing. In general, making
+    assumptions can be dangerous; you can break your application. <i>Only use
+    this option if you know what you're doing!</i></dd>
+
+<dt><a name="allowaccessmodification"><code><b>-allowaccessmodification</b></code></a></dt>
+
+<dd>Specifies that the access modifiers of classes and class members may be
+    modified during processing. This can improve the results of the
+    optimization step. For instance, when inlining a public getter, it may be
+    necessary to make the accessed field public too. Although Java's binary
+    compatibility specifications (cfr. <a href=
+    "http://java.sun.com/docs/books/jls/second_edition/html/j.title.doc.html"
+    >The Java Language Specification, Second Edition</a>, <a href=
+    "http://java.sun.com/docs/books/jls/second_edition/html/binaryComp.doc.html#47259"
+    >Section 13.4.6</a>) formally do not require this, some virtual machines
+    would have problems with the processed code otherwise. Only applicable
+    when optimizing.</dd>
+
+</dl>
+<p>
+
+<a name="obfuscationoptions"> </a>
+<h2>Obfuscation Options</h2>
+
+<dl>
+<dt><a name="dontobfuscate"><code><b>-dontobfuscate</b></code></a></dt>
+
+<dd>Specifies not to obfuscate the input class files. By default, obfuscation
+    is applied; classes and class members receive new short random names,
+    except for the ones listed by the various <code>-keep</code> options.
+    Internal attributes that are useful for debugging, such as source files
+    names, variable names, and line numbers are removed.</dd>
+
+<dt><a name="printmapping"><code><b>-printmapping</b></code></a>
+    [<a href="#filename"><i>filename</i></a>]</dt>
+
+<dd>Specifies to print the mapping from old names to new names for classes and
+    class members that have been renamed. The mapping is printed to the
+    standard output or to the given file. It is required for subsequent
+    incremental obfuscation, or if you ever want to make sense again of <a
+    href="examples.html#stacktrace">obfuscated stack traces</a>. Only
+    applicable when obfuscating.</dd>
+
+<dt><a name="applymapping"><code><b>-applymapping</b></code></a>
+    <a href="#filename"><i>filename</i></a></dt>
+
+<dd>Specifies to reuse the given name mapping that was printed out in a
+    previous obfuscation run of ProGuard. Classes and class members that are
+    listed in the mapping file receive the names specified along with them.
+    Classes and class members that are not mentioned receive new names. The
+    mapping may refer to input classes as well as library classes. This option
+    can be useful for <a href="examples.html#incremental">incremental
+    obfuscation</a>, i.e. processing add-ons or small patches to an existing
+    piece of code. Only applicable when obfuscating.</dd>
+
+<dt><a name="obfuscationdictionary"><code><b>-obfuscationdictionary</b></code></a>
+    <a href="#filename"><i>filename</i></a></dt>
+
+<dd>Specifies a text file from which all valid words are used as obfuscated
+    method names. White space, punctuation characters, duplicate words, and
+    comments after a <code><b>#</b></code> sign are ignored. By default, short
+    names like 'a', 'b', etc. are used as obfuscated method names. With an
+    obfuscation dictionary, you can specify a list of reserved key words, or
+    identifiers with foreign characters, for instance. Note that this hardly
+    improves the obfuscation. Decent compilers can automatically replace them,
+    and the effect can fairly simply be undone by obfuscating again with
+    simpler names. The most useful application is specifying strings that are
+    typically already present in class files (such as 'Code' and
+    'Exceptions'), thus reducing the class file sizes just a little bit more.
+    Only applicable when obfuscating.</dd>
+
+<dt><a name="overloadaggressively"><code><b>-overloadaggressively</b></code></a></dt>
+
+<dd>Specifies to apply aggressive overloading while obfuscating. Multiple
+    fields and methods can then get the same names, as long as their arguments
+    and return types are different (not just their arguments). This option can
+    make the output jar even smaller (and less comprehensible). Only
+    applicable when obfuscating.
+    <p>
+    Counter-indications: the resulting class files fall within the Java
+    bytecode specification (cfr. <a href=
+    "http://java.sun.com/docs/books/vmspec/2nd-edition/html/VMSpecTOC.doc.html"
+    >The Java Virtual Machine Specification, Second Edition</a>, first
+    paragraphs of <a href=
+    "http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#2877"
+    >Section 4.5</a> and <a href=
+    "http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#1513"
+    >Section 4.6</a>), even though this kind of overloading is not allowed in
+    the Java language (cfr. <a href=
+    "http://java.sun.com/docs/books/jls/second_edition/html/j.title.doc.html"
+    >The Java Language Specification, Second Edition</a>, <a href=
+    "http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#40898"
+    >Section 8.3</a> and <a href=
+    "http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#227768"
+    >Section 8.4.7</a>). Still, some tools have problems with it. Notably,
+    Sun's JREs 1.4 and later fail to serialize objects with overloaded
+    primitive fields. Also, Sun's JDK 1.2.2 javac compiler produces an
+    exception when compiling with such a library (cfr. <a href=
+    "http://developer.java.sun.com/developer/bugParade/bugs/4216736.html">Bug
+    #4216736</a>). You therefore probably shouldn't use this option for
+    processing libraries.</dd>
+
+<dt><a name="defaultpackage"><code><b>-defaultpackage</b></code></a>
+    [<i>package_name</i>]</dt>
+
+<dd>Specifies to repackage all class files that are renamed into the single
+    given package. Without argument or with an empty string (''), the package
+    is removed completely. This option can make the output jar even smaller
+    (and less comprehensible). Only applicable when obfuscating.
+    <p>
+    This option implies the <a
+    href="#allowaccessmodification"><code><b>-allowaccessmodification</b></code></a>
+    option. In this case, ProGuard avoids access permissions becoming
+    inconsistent, by making all package visible classes and class members
+    public. These changes don't affect the functioning of applications. It
+    might be confusing and therefore less desirable when processing libraries.
+    <p>
+    Counter-indications: classes that look for resource files in their package
+    directories will no longer work properly if they are moved elsewhere. When
+    in doubt, just leave the packaging untouched by not using this
+    option.</dd>
+
+<dt><a name="dontusemixedcaseclassnames"><code><b>-dontusemixedcaseclassnames</b></code></a></dt>
+
+<dd>Specifies not to generate mixed-case class names while obfuscating. By
+    default, obfuscated class names can contain a mix of upper-case characters
+    and lower-case characters. This creates perfectly acceptable and usable
+    jars. Only if a jar is unpacked on a platform with a case-insensitive
+    filing system (say, Windows), the unpacking tool may let similarly named
+    class files overwrite each other. Code that self-destructs when it's
+    unpacked! Developers who really want to unpack their jars on Windows can
+    use this option to switch off this behavior. Note that the obfuscated jars
+    will become larger as a result. Only applicable when obfuscating.</dd>
+
+<dt><a name="keepattributes"><code><b>-keepattributes</b></code></a>
+    [<i>attribute_name<b>,</b>...</i>]</dt>
+
+<dd>Specifies any optional attributes to be preserved. The attributes can be
+    specified with one or more <code>-keepattributes</code> directives.
+    Multiple attributes should be separated by commas. An empty list preserves
+    all attributes. Attribute names can contain <b>?</b> and
+    <b>*</b> wildcards, and they can be preceded by the <b>!</b> negator (much
+    like file name filters). Typical optional attributes are
+    <code>LineNumberTable</code>, <code>LocalVariableTable</code>,
+    <code>LocalVariableTypeTable</code>, <code>SourceFile</code>,
+    <code>SourceDir</code>, <code>Deprecated</code>, <code>Synthetic</code>,
+    <code>Signature</code>, <code>EnclosingMethod</code>,
+    <code>RuntimeVisibleAnnotations</code>,
+    <code>RuntimeInvisibleAnnotations</code>,
+    <code>RuntimeVisibleParameterAnnotations</code>,
+    <code>RuntimeInvisibleParameterAnnotations</code>, and
+    <code>AnnotationDefault</code>. The <code>InnerClasses</code> attribute
+    name can be specified as well, referring to the source name part of this
+    attribute. For example, you could keep the <code>Deprecated</code>
+    attribute when <a href="examples.html#library">processing a library</a>.
+    Only applicable when obfuscating.</dd>
+
+<dt><a name="renamesourcefileattribute"><code><b>-renamesourcefileattribute</b></code></a>
+    [<i>string</i>]</dt>
+
+<dd>Specifies a constant string to be put in the <code>SourceFile</code>
+    attributes (and <code>SourceDir</code> attributes) of the class files.
+    Note that the attribute has to be present to start with, so it also has to
+    be preserved explicitly using the <code>-keepattributes</code> directive.
+    For example, you may want to have your processed libraries and
+    applications produce <a href="examples.html#stacktrace">useful obfuscated
+    stack traces</a>. Only applicable when obfuscating.</dd>
+
+</dl>
+<p>
+
+<a name="generaloptions"> </a>
+<h2>General Options</h2>
+
+<dl>
+<dt><a name="verbose"><code><b>-verbose</b></code></a></dt>
+
+<dd>Specifies to write out some more information during processing. If the
+    program terminates with an exception, this option will print out the entire
+    stack trace, instead of just the exception message.</dd>
+
+<dt><a name="dontnote"><code><b>-dontnote</b></code></a></dt>
+
+<dd>Specifies not to print notes about class casts of variable dynamically
+    created objects. These notes provide hints about classes that may have to
+    be kept.</dd>
+
+<dt><a name="dontwarn"><code><b>-dontwarn</b></code></a></dt>
+
+<dd>Specifies not to warn about unresolved references at all. Again, if the
+    unresolved classes or class members are indeed required for processing,
+    the output jar will not function properly. <i>Only use this option if you
+    know what you're doing!</i></dd>
+
+<dt><a name="ignorewarnings"><code><b>-ignorewarnings</b></code></a></dt>
+
+<dd>Specifies to print any warnings about unresolved references to
+    superclasses, interfaces, or class members, but to continue processing in
+    any case. If the classes or class members are indeed required for
+    processing, the output jar will not function properly. <i>Only use this
+    option if you know what you're doing!</i></dd>
+
+<dt><a name="dump"><code><b>-dump</b></code></a>
+    [<a href="#filename"><i>filename</i></a>]</dt>
+
+<dd>Specifies to write out the internal structure of the class files, after
+    any processing. The structure is printed to the standard output or to the
+    given file. For example, you may want to <a
+    href="examples.html#structure">write out the contents of a given jar
+    file</a>, without shrinking or obfuscating it first.</dd>
+
+</dl>
+<p>
+
+<a name="classpath"> </a>
+<h2>Class Paths</h2>
+
+ProGuard accepts a generalisation of class paths to specify input files and
+output files. A class path consists of entries, separated by the path
+separator (e.g. '<b>:</b>' on Unix, or '<b>;</b>' on Windows platforms). The
+order of the entries determines their priorities, in case of duplicates.
+<p>
+Each input entry can be:
+<ul>
+<li>A class file or resource file.
+<li>A jar file, containing any of the above,
+<li>A war file, containing any of the above,
+<li>An ear file, containing any of the above,
+<li>A zip file, containing any of the above,
+<li>A directory (structure), containing any of the above.
+</ul>
+<p>
+The paths of directly specified class files and resource files is ignored, so
+class files should generally be part of a jar file, a war file, an ear file, a
+zip file, or a directory. In addition, the paths of class files should not have
+any additional directory prefixes inside the archives or directories.
+
+<p>
+Each output entry can be:
+<ul>
+<li>A jar file, in which all processed class files and resource files will be
+    collected.
+<li>A war file, in which any and all of the above will be collected,
+<li>An ear file, in which any and all of the above will be collected,
+<li>A zip file, in which any and all of the above will be collected,
+<li>A directory, in which any and all of the above will be collected.
+</ul>
+<p>
+When writing output entries, ProGuard will generally package the results in a
+sensible way, reconstructing the input entries as much as required. Writing
+everything to an output directory is the most straightforward option: the
+output directory will contain a complete reconstruction of the input entries.
+The packaging can be almost arbitrarily complex though: you could process an
+entire application, packaged in a zip file along with its documentation,
+writing it out as a zip file again.
+<p>
+Files and directories can be specified as discussed in the section on <a
+href="#filename">file names</a> below.
+<p>
+In addition, the individual class patch entries and their contents can be
+filtered, as explained in the <a href="#filters">filters</a> section below.
+This allows for an almost infinite number of packaging and repackaging
+possibilities.
+<p>
+
+<a name="filename"> </a>
+<h2>File Names</h2>
+
+ProGuard accepts absolute paths and relative paths for the various file names
+and directory names.
+<p>
+The names can contain Java system properties delimited by '<b><</b>' and
+'<b>></b>'. The system properties
+are automatically replaced by their respective values.
+<p>
+For example, <code><java.home>/lib/rt.jar</code> will automatically be
+expanded to something like <code>/usr/local/java/jdk/jre/lib/rt.jar</code>.
+<p>
+Names with special characters like spaces and parentheses must be quoted
+with single or double quotes. Note that each file name in a list of names has
+to be quoted individually. Also note that the quotes may need to be escaped
+when used on the command line, to avoid them being gobbled by the shell.
+<p>
+For example, on the command line, you could use an option like <code>'-injars
+"my program.jar":"/your directory/your program.jar"'</code>.
+<p>
+
+<a name="filters"> </a>
+<h2>Filters</h2>
+
+ProGuard provides the possibility to filter the class path entries and their
+contents, based on their full relative file names. Each class path entry can
+be followed by up to 5 types of filters between parentheses, separated by
+semi-colons:
+<ul>
+<li>A filter for all zip names that are encountered,
+<li>A filter for all ear names that are encountered,
+<li>A filter for all war names that are encountered,
+<li>A filter for all jar names that are encountered,
+<li>A filter for all class file names and resource file names that are
+    encountered.
+</ul>
+<p>
+If fewer than 5 filters are specified, they are assumed to be the latter
+filters. Any empty filters are ignored. More formally, a filtered class path
+entry looks like this:
+<pre>
+<i>classpathentry</i><b>(</b>[[[[<i>zipfilter</i><b>;</b>]<i>earfilter</i><b>;</b>]<i>warfilter</i><b>;</b>]<i>jarfilter</i><b>;</b>]<i>filefilter</i><b>)</b>
+</pre>
+<p>
+Square brackets "[]" mean that their contents are optional.
+
+<p>
+A filter consists of one or more comma-separated file names that can contain
+wildcards. Only files with matching file names are read (in the case of input
+jars), or written (in the case of output jars). The following wildcards are
+supported:
+
+<table cellspacing="10">
+<tr><td valign="top"><code><b>?</b></code></td>
+    <td>matches any single character in a file name.</td></tr>
+<tr><td valign="top"><code><b>*</b></code></td>
+    <td>matches any part of a filename not containing the directory
+        separator.</td></tr>
+<tr><td valign="top"><code><b>**</b></code></td>
+    <td>matches any part of a filename, possibly containing any number of
+        directory separators.</td></tr>
+</table>
+
+For example, "<code>rt.jar(java/**.class,javax/**.class)</code>" matches all
+class files in the <code>java</code> and <code>javax</code> directories inside
+the <code>rt</code> jar.
+<p>
+
+Furthermore, a file name can be preceded by an exclamation mark '<b>!</b>' to
+<i>exclude</i> the file name from further attempts to match with
+<i>subsequent</i> file names. You can think of the filtering mechanism as a
+conveyor belt, with pushers that accept or reject files as they pass by.
+<p>
+For example, "<code>input.jar(!**.gif,images/**)</code>" matches all files in
+the <code>images</code> directory inside the <code>input</code> jar, except
+gif files.
+<p>
+
+Note that the different filters are applied to all corresponding file types,
+irrespective of their nesting levels in the input; they are orthogonal.
+<p>
+For example,
+"<code>input.war(lib/**.jar,support/**.jar;**.class,**.gif)</code>" only
+considers jar files in the <code>lib</code> and <code>support</code>
+directories in the <code>input</code> war, not any other jar files. It then
+matches all class files and gif files that are encountered.
+
+<a name="keepoverview"> </a>
+<h2>Overview of <code>Keep</code> Options</h2>
+
+The various <code>-keep</code> options for shrinking and obfuscation may seem
+a bit confusing at first, but there's actually a pattern behind them. The
+following table summarizes how they are related:
+<p>
+
+<table cellpadding="5">
+
+<tr>
+<th>Keep</th>
+<td>From being removed or renamed</td>
+<td>From being renamed</td>
+</tr>
+
+<tr>
+<td>Classes and class members</td>
+<td bgcolor="#E0E0E0"><code>-keep</code></td>
+<td bgcolor="#E0E0E0"><code>-keepnames</code></td>
+</tr>
+
+<tr>
+<td>Class members only</td>
+<td bgcolor="#E0E0E0"><code>-keepclassmembers</code></td>
+<td bgcolor="#E0E0E0"><code>-keepclassmembernames</code></td>
+</tr>
+
+<tr>
+<td>Classes and class members, if class members present</td>
+<td bgcolor="#E0E0E0"><code>-keepclasseswithmembers</code></td>
+<td bgcolor="#E0E0E0"><code>-keepclasseswithmembernames</code></td>
+</tr>
+
+</table>
+<p>
+
+Each of these <code>-keep</code> options is of course followed by a
+<a href="#classspecification">specification</a> of the classes and class
+members (fields and methods) to which it should be applied.
+<p>
+If you're not sure which option you need, you should probably simply use
+<code>-keep</code>. It will make sure the specified classes and class members
+are not removed in the shrinking step, and not renamed in the obfuscation step.
+<p>
+
+<a name="classspecification"> </a>
+<h2>Class Specifications</h2>
+
+A class specification is a template of classes and class members (fields and
+methods). It is used in the various <code>-keep</code> options and in the
+<code>-assumenosideeffects</code> option. The corresponding option is only
+applied to classes and class members that match the template.
+<p>
+The template was designed to look very Java-like, with some extensions for
+wildcards. To get a feel for the syntax, you should probably look at the <a
+href="examples.html">examples</a>, but this is an attempt at a complete formal
+definition:
+<p>
+
+<pre>
+[[<b>!</b>]<b>public</b>|<b>final</b>|<b>abstract</b> ...] ([<b>!</b>]<b>interface</b>)|<b>class</b> <i>classname</i>
+    [<b>extends</b>|<b>implements</b> <i>classname</i>]
+[<b>{</b>
+    [[<b>!</b>]<b>public</b>|<b>private</b>|<b>protected</b>|<b>static</b>|<b>volatile</b>|<b>transient</b> ...] <b><fields></b> |
+                                                                      (<i>fieldtype fieldname</i>)<b>;</b>
+    [[<b>!</b>]<b>public</b>|<b>private</b>|<b>protected</b>|<b>static</b>|<b>synchronized</b>|<b>native</b>|<b>abstract</b>|<b>strictfp</b> ...] <b><methods></b> |
+                                                                                           <b><init>(</b><i>argumenttype,...</i><b>)</b> |
+                                                                                           <i>classname</i><b>(</b><i>argumenttype,...</i><b>)</b> |
+                                                                                           (<i>returntype methodname</i><b>(</b><i>argumenttype,...</i><b>)</b>)<b>;</b>
+    [[<b>!</b>]<b>public</b>|<b>private</b>|<b>protected</b>|<b>static</b> ... ] <b>*;</b>
+    ...
+<b>}</b>]
+</pre>
+<p>
+Square brackets "[]" mean that their contents are optional. Ellipsis dots
+"..." mean that any number of the preceding items may be specified. A vertical
+bar "|" delimits two alternatives. Non-bold parentheses "()" just group parts
+of the specification that belong together. The indentation tries to clarify
+the intended meaning, but white-space is irrelevant in actual configuration
+files.
+<p>
+<ul>
+
+<li>The <code><b>class</b></code> keyword refers to any interface or class.
+    The <code><b>interface</b></code> keyword restricts matches to interface
+    classes. Preceding the <code><b>interface</b></code> keyword by a
+    <code><b>!</b></code> restricts matches to classes that are not
+    interfaces.
+    <p>
+
+<li>Every <i>classname</i> must be fully qualified, e.g.
+    <code>java.lang.String</code>. Class names may be specified as regular
+    expressions containing the following wildcards:
+
+<table cellspacing="10">
+
+<tr><td valign="top"><code><b>?</b></code></td>
+
+<td>matches any single character in a class name, but not the package
+    separator. For example, "<code>mypackage.Test?</code>" matches
+    "<code>mypackage.Test1</code>" and "<code>mypackage.Test2</code>", but not
+    "<code>mypackage.Test12</code>".</td></tr>
+
+<tr><td valign="top"><code><b>*</b></code></td>
+
+<td>matches any part of a class name not containing the package separator. For
+    example, "<code>mypackage.*Test*</code>" matches
+    "<code>mypackage.Test</code>" and
+    "<code>mypackage.YourTestApplication</code>", but not
+    "<code>mypackage.mysubpackage.MyTest</code>". Or, more generally,
+    "<code>mypackage.*</code>" matches all classes in
+    "<code>mypackage</code>", but not in its subpackages.</td></tr>
+
+<tr><td valign="top"><code><b>**</b></code></td>
+
+<td>matches any part of a class name, possibly containing any number of
+    package separators. For example, "<code>**.Test</code>" matches all
+    <code>Test</code> classes in all packages except the root package. Or,
+    "<code>mypackage.**</code>" matches all classes in
+    "<code>mypackage</code>" and in its subpackages.</td></tr>
+
+</table>
+
+    For convenience and for backward compatibility, the class name
+    <code><b>*</b></code> refers to any class, irrespective of its package.
+    <p>
+
+<li>The <code><b>extends</b></code> and <code><b>implements</b></code>
+    specifications are typically used to restrict classes with wildcards. They
+    are currently equivalent, specifying that only classes extending or
+    implementing the given class qualify. Note that the given class itself is
+    not included in this set. If required, it should be specified in a
+    separate option.
+    <p>
+
+<li>Fields and methods are specified much like in Java, except that method
+    argument lists don't contain argument names (as is common in other tools
+    like <code>javadoc</code> and <code>javap</code>). The specifications can
+    also contain the following catch-all wildcards:
+
+<table cellspacing="10">
+
+<tr><td valign="top"><code><b><init></b></code></td>
+<td>matches any constructor.</td></tr>
+
+<tr><td valign="top"><code><b><fields></b></code></td>
+<td>matches any field.</td></tr>
+
+<tr><td valign="top"><code><b><methods></b></code></td>
+<td>matches any method.</td></tr>
+
+<tr><td valign="top"><code><b>*</b></code></td>
+<td>matches any field or method.</td></tr>
+
+</table>
+
+    Note that the above wildcards don't have return types. Only the
+    <code><b><init></b></code> wildcard has an argument list.
+    <p>
+
+    Fields and methods may also be specified using regular expressions. Names
+    can contain the following wildcards:
+
+<table cellspacing="10">
+<tr><td valign="top"><code><b>?</b></code></td>
+    <td>matches any single character in a method name.</td></tr>
+<tr><td valign="top"><code><b>*</b></code></td>
+    <td>matches any part of a method name.</td></tr>
+</table>
+
+    Types in descriptors can contain the following wildcards:
+
+<table cellspacing="10">
+<tr><td valign="top"><code><b>?</b></code></td>
+    <td>matches any single character in a class name.</td></tr>
+<tr><td valign="top"><code><b>*</b></code></td>
+    <td>matches any part of a class name not containing the package                     separator.</td></tr>
+<tr><td valign="top"><code><b>**</b></code></td>
+    <td>matches any part of a class name, possibly containing any number of
+        package separators.</td></tr>
+<tr><td valign="top"><code><b>%</b></code></td>
+    <td>matches any primitive type ("<code>boolean</code>", "<code>int</code>",
+        etc, but not "<code>void</code>").</td></tr>
+</table>
+
+    Note that the <code>?</code>, <code>*</code>, and <code>**</code>
+    wildcards will never match primitive types. None of the wildcards will
+    match array types of different dimensions than specified by the regular
+    expression. For example, "<code>** get*()</code>" matches
+    "<code>java.lang.Object getObject()</code>", but not "<code>float
+    getFloat()</code>", nor "<code>java.lang.Object[] getObjects()</code>".
+    <p>
+
+<li>Constructors can also be specified using their short class names (without
+    package) or using their full class names. As in the Java language, the
+    constructor specification has an argument list, but no return type.
+    <p>
+
+<li>The class access modifiers and class member access modifiers are typically
+    used to restrict wildcarded classes and class members. They specify that
+    the corresponding access flags have to be set for the member to match. A
+    preceding <code><b>!</b></code> specifies that the corresponding access
+    flag should be unset.
+    <p>
+    Combining multiple flags is allowed (e.g. <code>public static</code>). It
+    means that both access flags have to be set (e.g. <code>public</code>
+    <i>and</i> <code>static</code>), except when they are conflicting, in
+    which case at least one of them has to be set (e.g. at least
+    <code>public</code>
+    <i>or</i> <code>protected</code>).
+
+</ul>
+<p>
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
diff --git a/docs/manual/wtk.html b/docs/manual/wtk.html
new file mode 100644
index 0000000..5c3325f
--- /dev/null
+++ b/docs/manual/wtk.html
@@ -0,0 +1,58 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard J2ME Wireless Toolkit Integration</title>
+</head>
+<body>
+
+<h2>J2ME Wireless Toolkit Integration</h2>
+
+<b>ProGuard</b> can be seamlessly integrated in the Sun J2ME Wireless Toolkit
+(WTK).
+<p>
+
+The WTK already comes with a plug-in for ProGuard. Alternatively, ProGuard
+offers its own implementation. The latter implementation solves some problems
+and is somewhat more efficient. It invokes the ProGuard engine directly,
+instead of writing out a configuration file and running ProGuard in a separate
+virtual machine.
+<p>
+
+In order to integrate this plug-in in the toolkit, you'll have to put the
+following lines in the file
+{j2mewtk.dir}<code>/wtklib/Linux/ktools.properties</code> or
+{j2mewtk.dir}<code>\wtklib\Windows\ktools.properties</code> (whichever is
+applicable).
+<p>
+
+<pre>
+obfuscator.runner.class.name: proguard.wtk.ProGuardObfuscator
+obfuscator.runner.classpath: /usr/local/java/proguard/lib/proguard.jar
+</pre>
+<p>
+
+Please make sure the class path is set correctly for your system.
+<p>
+
+Once ProGuard has been set up, you can apply it to your projects as part of
+the build process. The build process is started from the WTK menu bar:
+<p>
+<center><b>Project -> Package -> Create Obfuscated Package</b></center>
+<p>
+This option will compile, shrink, obfuscate, verify, and install your midlets
+for testing.
+<p>
+Should you ever need to customize your ProGuard configuration for the J2ME WTK,
+you can adapt the configuration file <code>proguard/wtk/default.pro</code>
+that's inside the <code>proguard.jar</code>.
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
diff --git a/docs/quality.html b/docs/quality.html
new file mode 100644
index 0000000..fe8f4df
--- /dev/null
+++ b/docs/quality.html
@@ -0,0 +1,37 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard Quality</title>
+</head>
+<body>
+
+<h2>Quality</h2>
+
+In order to get a feel for the quality of the <b>ProGuard</b> code, it is run
+through a nightly automatic build process. This process produces numerous
+statistics on the source code, Java lint comments, Java documentation
+comments, the Java documentation itself, html lint comments on the Java
+documentation, spell checks, compilation results, an output jar, dead code
+analysis, a shrunk and obfuscated jar (using ProGuard itself!), test runs with
+memory and performance analyses, etc. Most analyses are produced using freely
+available tools. The results are poured into a convenient set of web pages
+using bash/sed/awk scripts. You're welcome to have a look at them:
+<p>
+<center><a href="http://proguard.sourceforge.net/quality/"
+target="other">Automated Code Analysis and Testing Pages</a> (at <a
+href="http://sourceforge.net/projects/proguard/"
+target="other">SourceForge</a>)</center>
+<p>
+The pages will appear in a new window, which you probably want to view at
+full-screen size.
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
diff --git a/docs/results.html b/docs/results.html
new file mode 100644
index 0000000..b62a629
--- /dev/null
+++ b/docs/results.html
@@ -0,0 +1,134 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard Results</title>
+</head>
+<body>
+
+<h2>Results</h2>
+
+<b>ProGuard</b> shrinks, optimizes, and obfuscates jars quickly and
+effectively. The benefits obviously depend on the original code. The table
+below presents some typical results:
+<p>
+
+<table>
+
+<tr>
+<th width="28%">Input Program</th>
+<th width="12%">Original size</th>
+<th width="12%">After shrinking</th>
+<th width="12%">After optim.</th>
+<th width="12%">After obfusc.</th>
+<th width="12%">Total reduction</th>
+<th width="12%">Time</th>
+<th width="12%">Memory usage</th>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.gnu.org/software/classpath/">ClassPath</a>, the GNU runtime library</td>
+<td align="center">2.0 M</td>
+<td align="center">2.0 M</td>
+<td align="center">1.9 M</td>
+<td align="center">1.9 M</td>
+<td align="center">4 %</td>
+<td align="center">31 s</td>
+<td align="center">39 M</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://java.sun.com/j2me/">Worm</a>, a sample midlet from Sun's J2ME</td>
+<td align="center">9.9 K</td>
+<td align="center">9.4 K</td>
+<td align="center">9.3 K</td>
+<td align="center">8.1 K</td>
+<td align="center">17 %</td>
+<td align="center">2 s</td>
+<td align="center">13 M</td>
+</tr>
+
+<tr>
+<td><b>ProGuard</b> itself</td>
+<td align="center">404 K</td>
+<td align="center">364 K</td>
+<td align="center">361 K</td>
+<td align="center">231 K</td>
+<td align="center">42 %</td>
+<td align="center">21 s</td>
+<td align="center">35 M</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.clarkware.com/software/JDepend.html">JDepend</a>, a Java quality metrics tool</td>
+<td align="center">57 K</td>
+<td align="center">36 K</td>
+<td align="center">35 K</td>
+<td align="center">30 K</td>
+<td align="center">48 %</td>
+<td align="center">8 s</td>
+<td align="center">24 M</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://jakarta.apache.org/tomcat/index.html">Tomcat</a>, the Apache servlet container</td>
+<td align="center">1.2 M</td>
+<td align="center">538 K</td>
+<td align="center">528 K</td>
+<td align="center">363 K</td>
+<td align="center">68 %</td>
+<td align="center">20 s</td>
+<td align="center">39 M</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://www.kclee.com/clemens/java/javancss/">JavaNCSS</a>, a Java source metrics tool</td>
+<td align="center">624 K</td>
+<td align="center">243 K</td>
+<td align="center">235 K</td>
+<td align="center">158 K</td>
+<td align="center">74 %</td>
+<td align="center">13 s</td>
+<td align="center">35 M</td>
+</tr>
+
+<tr>
+<td><a target="other" href="http://ant.apache.org/">Ant</a>, the Apache build tool</td>
+<td align="center">2.3 M</td>
+<td align="center">276 K</td>
+<td align="center">265 K</td>
+<td align="center">203 K</td>
+<td align="center">91 %</td>
+<td align="center">20 s</td>
+<td align="center">49 M</td>
+</tr>
+
+</table>
+<p>
+Results were measured with ProGuard 3.0 on a 2.6 GHz Pentium 4 with 512 MB
+of memory, using Sun JDK 1.4.2 in Fedora Core 1 Linux.
+<p>
+The program sizes include companion libraries. The shrinking step produces the
+best results for programs that use only small parts of their libraries. The
+obfuscation step can significantly shrink large programs even further, since
+the identifiers of their many internal references can be replaced by short
+identifiers.
+<p>
+Timings are mainly governed by the fixed overhead of reading jars and
+initializing data structures. The actual shrinking, optimization, and
+obfuscation are typically fast in comparison.
+<p>
+Memory usage (the amount of physical memory used by ProGuard while processing)
+is governed by the basic java virtual machine and the total size of the
+library jars and program jars.
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+
+</body>
+</html>
diff --git a/docs/screenshot_console.gif b/docs/screenshot_console.gif
new file mode 100644
index 0000000..f920538
Binary files /dev/null and b/docs/screenshot_console.gif differ
diff --git a/docs/screenshot_console_small.gif b/docs/screenshot_console_small.gif
new file mode 100644
index 0000000..f8cd9dc
Binary files /dev/null and b/docs/screenshot_console_small.gif differ
diff --git a/docs/screenshot_gui1.gif b/docs/screenshot_gui1.gif
new file mode 100644
index 0000000..68b1ee9
Binary files /dev/null and b/docs/screenshot_gui1.gif differ
diff --git a/docs/screenshot_gui2.gif b/docs/screenshot_gui2.gif
new file mode 100644
index 0000000..4ab61af
Binary files /dev/null and b/docs/screenshot_gui2.gif differ
diff --git a/docs/screenshot_gui3.gif b/docs/screenshot_gui3.gif
new file mode 100644
index 0000000..5805e39
Binary files /dev/null and b/docs/screenshot_gui3.gif differ
diff --git a/docs/screenshot_gui4.gif b/docs/screenshot_gui4.gif
new file mode 100644
index 0000000..9326203
Binary files /dev/null and b/docs/screenshot_gui4.gif differ
diff --git a/docs/screenshot_gui5.gif b/docs/screenshot_gui5.gif
new file mode 100644
index 0000000..ac510e9
Binary files /dev/null and b/docs/screenshot_gui5.gif differ
diff --git a/docs/screenshot_gui6.gif b/docs/screenshot_gui6.gif
new file mode 100644
index 0000000..f75027a
Binary files /dev/null and b/docs/screenshot_gui6.gif differ
diff --git a/docs/screenshot_gui7.gif b/docs/screenshot_gui7.gif
new file mode 100644
index 0000000..12e088c
Binary files /dev/null and b/docs/screenshot_gui7.gif differ
diff --git a/docs/screenshot_gui8.gif b/docs/screenshot_gui8.gif
new file mode 100644
index 0000000..f862ccf
Binary files /dev/null and b/docs/screenshot_gui8.gif differ
diff --git a/docs/screenshots.html b/docs/screenshots.html
new file mode 100644
index 0000000..61f401b
--- /dev/null
+++ b/docs/screenshots.html
@@ -0,0 +1,56 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard Screenshots</title>
+</head>
+<body>
+
+<h2>Screenshots</h2>
+
+<table><tr><td>
+<a href="screenshot_gui1.gif" target="other">
+<img src="screenshots_gui_small.gif" width="320" height="223" align="right"
+  alt="GUI screenshot" usemap="#guimap"></a>
+
+<map id="guimap" name="guimap">
+<area shape="rect" coords="2,7,46,16"  alt="ProGuard"     href="screenshot_gui1.gif" target="other">
+<area shape="rect" coords="2,17,46,27" alt="Input/Output" href="screenshot_gui2.gif" target="other">
+<area shape="rect" coords="2,28,46,37" alt="Shrinking"    href="screenshot_gui3.gif" target="other">
+<area shape="rect" coords="2,38,46,48" alt="Optimization" href="screenshot_gui4.gif" target="other">
+<area shape="rect" coords="2,49,46,58" alt="Obfuscation"  href="screenshot_gui5.gif" target="other">
+<area shape="rect" coords="2,59,46,69" alt="Information"  href="screenshot_gui6.gif" target="other">
+<area shape="rect" coords="2,70,46,79" alt="Process"      href="screenshot_gui7.gif" target="other">
+<area shape="rect" coords="2,80,46,90" alt="ReTrace"      href="screenshot_gui8.gif" target="other">
+</map>
+
+The graphical user interface to <b>ProGuard</b> works like a wizard. It allows
+you to browse through the presented tabs and fill them out.
+<p>
+You can click on the small tab buttons to see the full-size versions of the
+tabs.
+
+</td></tr>
+<tr><td>
+<a href="screenshot_console.gif" target="other">
+<img src="screenshot_console_small.gif" width="320" height="268" align="left"
+  alt="Console screenshot"></a>
+
+Of course, real developers don't need all this point-and-click fluff. They
+write a short configuration file using their favorite text editor and invoke
+<b>ProGuard</b> from the command-line.
+<p>
+You can click on the image to see the full-size version.
+
+</td></tr></table>
+<p>
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
diff --git a/docs/screenshots_gui_small.gif b/docs/screenshots_gui_small.gif
new file mode 100644
index 0000000..a367431
Binary files /dev/null and b/docs/screenshots_gui_small.gif differ
diff --git a/docs/sections.html b/docs/sections.html
new file mode 100644
index 0000000..39a1866
--- /dev/null
+++ b/docs/sections.html
@@ -0,0 +1,121 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-script-type" content="text/javascript">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>Sections</title>
+
+<script type="text/javascript" language="JavaScript">
+<!--
+function defineSection(aName, aTarget, aURL) {
+  document.write("<tr><th class=\"sections\"");
+  document.write(document.body != null ?
+    (" onMouseOver=\"lightUp(this)\" onMouseOut=\"lightDown(this)\" onClick=\"goTo(this,'"+aTarget+"','"+aURL+"')\" onAfterUpdate=\"lightUp(this)\">"+aName) :
+    ("><a target=\""+aTarget+"\" href=\""+aURL+"\">"+aName+"</a>"));
+  document.write("</th></tr>");
+}
+
+function lightUp(aTH) {
+  if (aTH != fTH)
+    aTH.bgColor = "#eeeeee";
+}
+function lightDown(aTH) {
+  if (aTH != fTH)
+    aTH.bgColor = "";
+}
+function goTo(aTH, aTarget, aURL) {
+  if (fTH != null)
+    fTH.bgColor = "";
+
+  fTH = aTH;
+  fTH.bgColor = "white";
+  f = findFrameByName(parent.frames, aTarget);
+  if (f != null)
+    f.document.location.href=aURL;
+}
+function findFrameByName(aFrames, aFrameName) {
+  for (i = 0; i < aFrames.length; i++)
+    if (aFrames[i].name == aFrameName)
+      return aFrames[i];
+  return null;
+}
+//-->
+</script>
+
+</head>
+<body class="title">
+
+<script type="text/javascript" language="JavaScript">
+<!--
+var fTH = null;
+//-->
+</script>
+
+<script type="text/javascript" language="JavaScript">
+<!--
+document.write("<table class=\"sections\" width=\"120\">");
+defineSection("Main",        "main",    "main.html");
+defineSection("Results",     "main",    "results.html");
+defineSection("FAQ",         "main",    "FAQ.html");
+defineSection("Manual",      "sections","manual/sections.html");
+defineSection("Quality",     "main",    "quality.html");
+defineSection("Screenshots", "main",    "screenshots.html");
+defineSection("Testimonials","main",    "testimonials.html");
+defineSection("License",     "main",    "license.html");
+defineSection("Downloads",   "main",    "downloads.html");
+defineSection("Feedback",    "main",    "feedback.html");
+defineSection("Ack'ments",   "main",    "acknowledgements.html");
+defineSection("Alternatives","main",    "alternatives.html");
+document.write("</table>");
+document.write("<p>");
+document.write("<center>");
+document.write("<small>With support of</small>");
+document.write("<p>");
+document.write("<a href=\"http://sourceforge.net/projects/proguard/\" target=\"other\">");
+document.write("<img src=\"");
+document.write(document.location.hostname == "proguard.sourceforge.net" ?
+  "http://sourceforge.net/sflogo.php?group_id=54750&type=1" :
+  "sflogo.png");
+document.write("\" width=\"88\" height=\"31\" alt=\"SourceForge\">");
+document.write("</a>");
+document.write("<p>");
+document.write("<a href=\"http://www.luciad.com/\" target=\"other\">");
+document.write("<img src=\"luciadlogo.png\" width=\"88\" height=\"24\" alt=\"Luciad\">");
+document.write("</a>");
+document.write("</center>");
+//-->
+</script>
+
+<noscript>
+<table class="sections">
+
+<tr><th class="sections"><a target="main" href="main.html">Main</a></th></tr>
+<tr><th class="sections"><a target="main" href="results.html">Results</a></th></tr>
+<tr><th class="sections"><a target="main" href="FAQ.html">FAQ</a></th></tr>
+<tr><th class="sections"><a               href="manual/sections.html">Manual</a></th></tr>
+<tr><th class="sections"><a target="main" href="quality.html">Quality</a></th></tr>
+<tr><th class="sections"><a target="main" href="screenshots.html">Screenshots</a></th></tr>
+<tr><th class="sections"><a target="main" href="testimonials.html">Testimonials</a></th></tr>
+<tr><th class="sections"><a target="main" href="license.html">License</a></th></tr>
+<tr><th class="sections"><a target="main" href="downloads.html">Downloads</a></th></tr>
+<tr><th class="sections"><a target="main" href="feedback.html">Feedback</a></th></tr>
+<tr><th class="sections"><a target="main" href="acknowledgements.html">Ack'ments</a></th></tr>
+<tr><th class="sections"><a target="main" href="alternatives.html">Alternatives</a></th></tr>
+
+</table>
+<p>
+<center>
+<small>With support of</small>
+<p>
+<a href="http://sourceforge.net/projects/proguard/" target="other">
+<img src="sflogo.png" width="88" height="31" alt="SourceForge"></a>
+<p>
+<a href="http://www.luciad.com/" target="other">
+<img src="luciadlogo.png" width="88" height="24" alt="Luciad"></a>
+</center>
+</noscript>
+
+</body>
+</html>
diff --git a/docs/sflogo.png b/docs/sflogo.png
new file mode 100644
index 0000000..1105bc7
Binary files /dev/null and b/docs/sflogo.png differ
diff --git a/docs/steel.gif b/docs/steel.gif
new file mode 100644
index 0000000..307b57a
Binary files /dev/null and b/docs/steel.gif differ
diff --git a/docs/style.css b/docs/style.css
new file mode 100644
index 0000000..b763813
--- /dev/null
+++ b/docs/style.css
@@ -0,0 +1,128 @@
+
+ at charset "iso-8859-1";
+
+/* Global settings. */
+
+body {
+  background: #FFFFFF;
+}
+
+h1 {
+  text-align: center;
+}
+
+h2 {
+  text-align: center;
+}
+
+h3 {
+  background: #EEEEFF;
+  padding: 10px;
+}
+
+table {
+  width: 100%;
+}
+
+th {
+  padding: 4px;
+}
+
+td {
+  background: #EEEEFF;
+  padding: 8px;
+}
+
+img {
+  border: none;
+}
+
+/* Settings for the introductory paragraph. */
+
+p.intro {
+  background: #EEEEFF;
+  padding: 10px;
+  border: solid #000000 1px
+}
+
+/* Settings for the title and section frames. */
+
+body.title {
+  margin: 0px;
+  background: #FFFFFF;
+}
+
+table.title {
+  width: 100%;
+  height: 50px;
+  background: url("steel.gif");
+  border: outset #FFFFFF 1px;
+  border-spacing: 0px;
+}
+
+th.title, td.title {
+  background: transparent;
+  border: none;
+  border-spacing: 0px;
+}
+
+.title a:link    { color: #000000; text-decoration: none; }
+.title a:visited { color: #000000; text-decoration: none; }
+.title a:hover   { color: #222222; text-decoration: none; }
+.title a:active  { color: #FFFFFF; text-decoration: none; }
+
+table.sections {
+  width: 100%;
+  height: 50px;
+  background: url("steel.gif");
+  border: 0px;
+  border-spacing: 0px;
+}
+
+th.sections, td.sections {
+  height: 40px;
+  border: outset #FFFFFF 1px;
+}
+
+/* Settings for the yellow note tables. */
+
+table.note {
+  width: 408px;
+  border: none;
+  border-spacing: 0px;
+}
+
+td.shadow8 {
+  width: 8px;
+  padding: 0px;
+  margin: 0px;
+  vertical-align: bottom;
+  background: transparent;
+}
+
+td.shadow400 {
+  width: 400px;
+  padding: 0px;
+  margin: 0px;
+  text-align: right;
+  background: transparent;
+}
+
+td.note {
+  width: 380px;
+  background: #FFFFC0;
+  padding: 0px;
+  margin: 0px;
+}
+
+p.note {
+  padding: 0px;
+  margin: 0px 10px;
+  text-align: center;
+}
+
+p.author {
+  padding: 0px;
+  margin: 0px 10px;
+  text-align: right;
+}
diff --git a/docs/testimonials.html b/docs/testimonials.html
new file mode 100644
index 0000000..87a4faa
--- /dev/null
+++ b/docs/testimonials.html
@@ -0,0 +1,98 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard Testimonials</title>
+</head>
+<body>
+
+<h2>Testimonials</h2>
+
+And now for some shameless self-glorification and name-dropping...
+<p>
+Since its first public release, <b>ProGuard</b> has been welcomed with general
+enthusiasm. It is already being used by developers at companies and
+organizations like Sun, IBM, Siemens, Nokia, and NATO. Although the quotes
+below probably don't represent official views of any kind, encouragements like
+these do keep me happy.
+<p>
+
+<center><table class="note">
+<tr><td class="note"><p class="note"><cite>
+ProGuard is <b>the</b> ultimate java obfuscator!
+</cite></p>
+<p class="author">P.S, IBM</p></td>
+<td class="shadow8"><img src="drop2.gif" width="8" height="100"></td></tr><tr>
+<td class="shadow400"><img src="drop1.gif" width="400" height="8"></td>
+<td class="shadow8"><img src="drop3.gif" width="8" height="8"></td>
+</tr></table></center>
+<p>
+
+And indeed:
+<center><table class="note">
+<tr><td class="note"><p class="note"><cite>
+ProGuard rules. Much easier to use than the commercial alternatives.
+</cite></p>
+<p class="author">B.G., Quiotix Corp.</p></td>
+<td class="shadow8"><img src="drop2.gif" width="8" height="100"></td></tr><tr>
+<td class="shadow400"><img src="drop1.gif" width="400" height="8"></td>
+<td class="shadow8"><img src="drop3.gif" width="8" height="8"></td>
+</tr></table></center>
+<p>
+
+Straight from <b>ProGuard</b>'s open discussion forum:
+<p>
+<center><table class="note">
+<tr><td class="note"><p class="note"><cite>
+After searching for, trying to trial, and futzing with numerous other
+obfuscators and shrinkers, ProGuard stands out as the simplest, most robust,
+and accurate shrinker of them all.
+</cite></p>
+<p class="author">D.J., Joot</p></td>
+<td class="shadow8"><img src="drop2.gif" width="8" height="100"></td></tr><tr>
+<td class="shadow400"><img src="drop1.gif" width="400" height="8"></td>
+<td class="shadow8"><img src="drop3.gif" width="8" height="8"></td>
+</tr></table></center>
+<p>
+
+From the article <a target="other"
+href="http://developers.sun.com/techtopics/mobility/midp/ttips/proguard/">"Obfuscating
+MIDlet Suites with ProGuard"</a> at <a target="other"
+href="http://developers.sun.com/">developers.sun.com</a>:
+<p>
+<center><table class="note">
+<tr><td class="note"><p class="note"><cite>
+Its friendly license, attractive price tag, compelling performance, and
+powerful configuration options make it an excellent addition to your MIDlet
+development toolbox.
+</cite></p>
+<p class="author">J.K., Sun</p></td>
+<td class="shadow8"><img src="drop2.gif" width="8" height="100"></td></tr><tr>
+<td class="shadow400"><img src="drop1.gif" width="400" height="8"></td>
+<td class="shadow8"><img src="drop3.gif" width="8" height="8"></td>
+</tr></table></center>
+<p>
+
+And, of course, the price is stunning:
+<p>
+<center><table class="note">
+<tr><td class="note"><p class="note"><cite>
+You could've been rich.
+</cite></p>
+<p class="author">My mother</p></td>
+<td class="shadow8"><img src="drop2.gif" width="8" height="100"></td></tr><tr>
+<td class="shadow400"><img src="drop1.gif" width="400" height="8"></td>
+<td class="shadow8"><img src="drop3.gif" width="8" height="8"></td>
+</tr></table></center>
+<p>
+
+<hr>
+<address>
+Copyright © 2002-2004
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+
+</body>
+</html>
diff --git a/docs/title.gif b/docs/title.gif
new file mode 100644
index 0000000..5e6ca26
Binary files /dev/null and b/docs/title.gif differ
diff --git a/docs/title.html b/docs/title.html
new file mode 100644
index 0000000..09f51c8
--- /dev/null
+++ b/docs/title.html
@@ -0,0 +1,19 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>ProGuard</title>
+</head>
+<body class="title">
+
+<table class="title">
+<tr>
+<th class="title"><h1><img src="title.gif" width="154" height="29" alt="ProGuard"></h1></th>
+<td class="title" width="100">Version 3.2</td>
+</tr>
+</table>
+
+</body>
+</html>
diff --git a/docs/vtitle.gif b/docs/vtitle.gif
new file mode 100644
index 0000000..cb2f070
Binary files /dev/null and b/docs/vtitle.gif differ
diff --git a/examples/ant/applets.xml b/examples/ant/applets.xml
new file mode 100644
index 0000000..a02dd3d
--- /dev/null
+++ b/examples/ant/applets.xml
@@ -0,0 +1,74 @@
+<!-- This Ant build file illustrates how to process applets.
+     Usage: ant -f applets.xml -->
+
+<project name="Applets" default="obfuscate" basedir="../..">
+
+<target name="obfuscate">
+  <taskdef resource="proguard/ant/task.properties"
+           classpath="lib/proguard.jar" />
+
+  <proguard printseeds="on">
+
+    <!-- Specify the input jars, output jars, and library jars. -->
+
+    <injar  file="in.jar" />
+    <outjar file="out.jar" />
+
+    <libraryjar file="${java.home}/lib/rt.jar" />
+
+    <!-- Preserve all public applets. -->
+
+    <keep access="public" extends="java.applet.Applet" />
+
+    <!-- Preserve all annotations. -->
+
+    <keepattribute name="*Annotation*" />
+
+    <!-- Preserve all native method names and the names of their classes. -->
+
+    <keepclasseswithmembernames>
+      <method access="native" />
+    </keepclasseswithmembernames>
+    
+    <!-- Preserve a method that is required in all enumeration classes. -->
+    
+    <keepclassmembers extends="java.lang.Enum">
+      <method access="public"
+              type="**[]"
+              name="values"
+              parameters="" />
+    </keepclassmembers>
+
+    <!-- Explicitly preserve all serialization members. The Serializable
+         interface is only a marker interface, so it wouldn't save them.
+         You can comment this out if your library doesn't use serialization.
+         If your code contains serializable classes that have to be backward
+         compatible, please refer to the manual. -->
+
+    <keepclassmembers implements="java.io.Serializable">
+      <field  access    ="final"
+              type      ="long"
+              name      ="serialVersionUID" />
+      <method access    ="private"
+              type      ="void"
+              name      ="writeObject"
+              parameters="java.io.ObjectOutputStream" />
+      <method access    ="private"
+              type      ="void"
+              name      ="readObject"
+              parameters="java.io.ObjectOutputStream" />
+      <method type      ="java.lang.Object"
+              name      ="writeReplace"
+              parameters="" />
+      <method type      ="java.lang.Object"
+              name      ="readResolve"
+              parameters="" />
+    </keepclassmembers>
+
+    <!-- Your application may contain more items that need to be preserved;
+         typically classes that are dynamically created using Class.forName -->
+
+   </proguard>
+</target>
+
+</project>
diff --git a/examples/ant/applications1.xml b/examples/ant/applications1.xml
new file mode 100644
index 0000000..12cf838
--- /dev/null
+++ b/examples/ant/applications1.xml
@@ -0,0 +1,15 @@
+<!-- This Ant build file illustrates how to process applications,
+     by including a ProGuard-style configuration file.
+     Usage: ant -f applications.xml -->
+
+<project name="Applications" default="obfuscate" basedir="../..">
+
+<target name="obfuscate">
+  <taskdef resource="proguard/ant/task.properties"
+           classpath="lib/proguard.jar" />
+
+  <proguard configuration="../applications.pro" />
+
+</target>
+
+</project>
diff --git a/examples/ant/applications2.xml b/examples/ant/applications2.xml
new file mode 100644
index 0000000..f887581
--- /dev/null
+++ b/examples/ant/applications2.xml
@@ -0,0 +1,66 @@
+<!-- This Ant build file illustrates how to process applications,
+     by including ProGuard-style configuration options.
+     Usage: ant -f applications.xml -->
+
+<project name="Applications" default="obfuscate" basedir="../..">
+
+<target name="obfuscate">
+  <taskdef resource="proguard/ant/task.properties"
+           classpath="lib/proguard.jar" />
+
+  <proguard>
+
+    <!-- Specify the input jars, output jars, and library jars. -->
+
+    -injars  in.jar
+    -outjars out.jar
+
+    -libraryjars ${java.home}/lib/rt.jar
+    <!-- -libraryjars junit.jar    -->
+    <!-- -libraryjars servlet.jar  -->
+    <!-- -libraryjars jai_core.jar -->
+    <!-- ...                       -->
+
+    <!-- Preserve all public applications. -->
+
+    -keepclasseswithmembers public class * {
+        public static void main(java.lang.String[]);
+    }
+
+    <!-- Preserve all annotations. -->
+
+    -keepattributes *Annotation*
+
+    <!-- Preserve all native method names and the names of their classes. -->
+
+    -keepclasseswithmembernames class * {
+        native <methods>;
+    }
+
+    <!-- Preserve a method that is required in all enumeration classes. -->
+
+    -keepclassmembers class * extends java.lang.Enum {
+        public **[] values();
+    }
+
+    <!-- Explicitly preserve all serialization members. The Serializable
+         interface is only a marker interface, so it wouldn't save them.
+         You can comment this out if your library doesn't use serialization.
+         If your code contains serializable classes that have to be backward
+         compatible, please refer to the manual. -->
+
+    -keepclassmembers class * implements java.io.Serializable {
+        static final long serialVersionUID;
+        private void writeObject(java.io.ObjectOutputStream);
+        private void readObject(java.io.ObjectInputStream);
+        java.lang.Object writeReplace();
+        java.lang.Object readResolve();
+    }
+
+    <!-- Your application may contain more items that need to be preserved;
+         typically classes that are dynamically created using Class.forName -->
+
+  </proguard>
+</target>
+
+</project>
diff --git a/examples/ant/applications3.xml b/examples/ant/applications3.xml
new file mode 100644
index 0000000..9edc8db
--- /dev/null
+++ b/examples/ant/applications3.xml
@@ -0,0 +1,85 @@
+<!-- This Ant build file illustrates how to process applications,
+     using a full-blown XML configuration.
+     Usage: ant -f applications.xml -->
+
+<project name="Applications" default="obfuscate" basedir="../..">
+
+<target name="obfuscate">
+  <taskdef resource="proguard/ant/task.properties"
+           classpath="lib/proguard.jar" />
+
+  <proguard printseeds="on">
+
+    <!-- Specify the input jars, output jars, and library jars. -->
+
+    <injar  file="in.jar" />
+    <outjar file="out.jar" />
+
+    <libraryjar file="${java.home}/lib/rt.jar" />
+    <!-- libraryjar file="junit.jar"           / -->
+    <!-- libraryjar file="servlet.jar"         / -->
+    <!-- libraryjar file="jai_core.jar"        / -->
+    <!-- ...                                   / -->
+
+
+    <!-- Preserve all public applications. -->
+
+    <keepclasseswithmembers access="public">
+      <method access    ="public static"
+              type      ="void"
+              name      ="main"
+              parameters="java.lang.String[]" />
+    </keepclasseswithmembers>
+
+    <!-- Preserve all annotations. -->
+
+    <keepattribute name="*Annotation*" />
+
+    <!-- Preserve all native method names and the names of their classes. -->
+
+    <keepclasseswithmembernames>
+      <method access="native" />
+    </keepclasseswithmembernames>
+
+    <!-- Preserve a method that is required in all enumeration classes. -->
+
+    <keepclassmembers extends="java.lang.Enum">
+      <method access="public"
+              type="**[]"
+              name="values"
+              parameters="" />
+    </keepclassmembers>
+
+    <!-- Explicitly preserve all serialization members. The Serializable
+         interface is only a marker interface, so it wouldn't save them.
+         You can comment this out if your library doesn't use serialization.
+         If your code contains serializable classes that have to be backward
+         compatible, please refer to the manual. -->
+
+    <keepclassmembers implements="java.io.Serializable">
+      <field  access    ="final"
+              type      ="long"
+              name      ="serialVersionUID" />
+      <method access    ="private"
+              type      ="void"
+              name      ="writeObject"
+              parameters="java.io.ObjectOutputStream" />
+      <method access    ="private"
+              type      ="void"
+              name      ="readObject"
+              parameters="java.io.ObjectOutputStream" />
+      <method type      ="java.lang.Object"
+              name      ="writeReplace"
+              parameters="" />
+      <method type      ="java.lang.Object"
+              name      ="readResolve"
+              parameters="" />
+    </keepclassmembers>
+
+    <!-- Your application may contain more items that need to be preserved;
+         typically classes that are dynamically created using Class.forName -->
+
+  </proguard>
+</target>
+
+</project>
diff --git a/examples/ant/library.xml b/examples/ant/library.xml
new file mode 100644
index 0000000..cb88e03
--- /dev/null
+++ b/examples/ant/library.xml
@@ -0,0 +1,95 @@
+<!-- This Ant build file illustrates how to process a program library,
+     such that it remains usable as a library.
+     Usage: ant -f library.xml -->
+
+<project name="Library" default="obfuscate" basedir="../..">
+
+<target name="obfuscate">
+  <taskdef resource="proguard/ant/task.properties"
+           classpath="lib/proguard.jar" />
+
+  <proguard printmapping="out.map"
+            renamesourcefileattribute="SourceFile">
+
+    <!-- Specify the input jars, output jars, and library jars. -->
+
+    <injar  file="library.jar" />
+    <outjar file="library_out.jar" />
+
+    <libraryjar file="${java.home}/lib/rt.jar" />
+
+    <!-- Keep some useful attributes. -->
+
+    <keepattribute name="InnerClasses" />
+    <keepattribute name="SourceFile" />
+    <keepattribute name="LineNumberTable" />
+    <keepattribute name="Deprecated" />
+    <keepattribute name="*Annotation*" />
+
+    <!-- Preserve all public classes, and their public and protected fields
+        and methods. -->
+
+    <keep access="public">
+      <field  access="public protected" />
+      <method access="public protected" />
+    </keep>
+
+    <!-- Preserve all .class method names. -->
+
+    <keepclassmembernames access="public">
+      <method type      ="java.lang.Class"
+              name      ="class$"
+              parameters="java.lang.String" />
+      <method type      ="java.lang.Class"
+              name      ="class$"
+              parameters="java.lang.String,boolean" />
+    </keepclassmembernames>
+
+    <!-- Preserve all native method names and the names of their classes. -->
+
+    <keepclasseswithmembernames>
+      <method access="native" />
+    </keepclasseswithmembernames>
+    
+    <!-- Preserve a method that is required in all enumeration classes. -->
+    
+    <keepclassmembers extends="java.lang.Enum">
+      <method access="public"
+              type="**[]"
+              name="values"
+              parameters="" />
+    </keepclassmembers>
+
+    <!-- Explicitly preserve all serialization members. The Serializable
+         interface is only a marker interface, so it wouldn't save them.
+         You can comment this out if your library doesn't use serialization.
+         If your code contains serializable classes that have to be backward
+         compatible, please refer to the manual. -->
+
+    <keepclassmembers implements="java.io.Serializable">
+      <field  access    ="final"
+              type      ="long"
+              name      ="serialVersionUID" />
+      <method access    ="private"
+              type      ="void"
+              name      ="writeObject"
+              parameters="java.io.ObjectOutputStream" />
+      <method access    ="private"
+              type      ="void"
+              name      ="readObject"
+              parameters="java.io.ObjectOutputStream" />
+      <method type      ="java.lang.Object"
+              name      ="writeReplace"
+              parameters="" />
+      <method type      ="java.lang.Object"
+              name      ="readResolve"
+              parameters="" />
+    </keepclassmembers>
+
+    <!-- Your application may contain more items that need to be preserved;
+      typically classes that are dynamically created using Class.forName -->
+
+  </proguard>
+</target>
+
+</project>
diff --git a/examples/ant/midlets.xml b/examples/ant/midlets.xml
new file mode 100644
index 0000000..5b8643c
--- /dev/null
+++ b/examples/ant/midlets.xml
@@ -0,0 +1,44 @@
+<!-- This Ant build file illustrates how to process J2ME midlets.
+     Usage: ant -f midlets.xml -->
+
+<project name="Midlets" default="obfuscate" basedir="../..">
+
+<target name="obfuscate">
+  <taskdef resource="proguard/ant/task.properties"
+           classpath="lib/proguard.jar" />
+
+  <proguard overloadaggressively="on"
+            defaultpackage=""
+            allowaccessmodification="on"
+            printseeds="on">
+
+            <!-- On Windows, you can't use mixed case class names,
+                 for the sake of the preverify tool.
+            usemixedcaseclassnames="false">
+            -->
+
+    <!-- Specify the input jars, output jars, and library jars. -->
+
+    <injar  file="in.jar" />
+    <outjar file="out.jar" />
+
+    <libraryjar file="/usr/local/java/wtk2.1/lib/midpapi20.jar" />
+    <libraryjar file="/usr/local/java/wtk2.1/lib/cldcapi11.jar" />
+
+    <!-- Preserve all public midlets. -->
+
+    <keep access="public" extends="javax.microedition.midlet.MIDlet" />
+
+    <!-- Preserve all native method names and the names of their classes. -->
+
+    <keepclasseswithmembernames>
+      <method access="native" />
+    </keepclasseswithmembernames>
+    
+    <!-- Your application may contain more items that need to be preserved;
+         typically classes that are dynamically created using Class.forName -->
+
+   </proguard>
+</target>
+
+</project>
diff --git a/examples/ant/proguard.xml b/examples/ant/proguard.xml
new file mode 100644
index 0000000..8cd44f0
--- /dev/null
+++ b/examples/ant/proguard.xml
@@ -0,0 +1,62 @@
+<!-- This Ant build file illustrates how to process ProGuard (including its
+     main application, its GUI, its Ant task, and its WTK plugin), and the
+     ReTrace tool, all in one go.
+     Usage: ant -f proguard.xml -->
+
+<project name="ProGuard" default="obfuscate" basedir="../..">
+
+<target name="obfuscate">
+  <taskdef resource="proguard/ant/task.properties"
+           classpath="lib/proguard.jar" />
+
+  <proguard printmapping="proguard.map"
+            overloadaggressively="on"
+            defaultpackage=""
+            allowaccessmodification="on">
+
+    <!-- Specify the input jars, output jars, and library jars. -->
+
+    <injar  file="lib/proguard.jar" />
+    <outjar file="proguard_out.jar" />
+
+    <libraryjar file="${java.home}/lib/rt.jar" />
+    <libraryjar file="/usr/local/java/ant1.6.2/lib/ant.jar" />
+    <libraryjar file="/usr/local/java/wtk2.1/wtklib/kenv.zip" />
+
+    <!-- The main seeds: ProGuard and its companion tool ReTrace. -->
+
+    <keep access="public" name="proguard.ProGuard">
+      <method access    ="public static"
+              type      ="void"
+              name      ="main"
+              parameters="java.lang.String[]" />
+    </keep>
+    <keep access="public" name="proguard.gui.ProGuardGUI">
+      <method access    ="public static"
+              type      ="void"
+              name      ="main"
+              parameters="java.lang.String[]" />
+    </keep>
+    <keep access="public" name="proguard.retrace.ReTrace">
+      <method access    ="public static"
+              type      ="void"
+              name      ="main"
+              parameters="java.lang.String[]" />
+    </keep>
+
+    <!-- If we have ant.jar, we can properly process the Ant task. -->
+
+    <keep access="public" name="proguard.ant.*">
+      <method access="public" type="void" name="set*" parameters="%"  />
+      <method access="public" type="void" name="set*" parameters="**" />
+      <method access="public" type="void" name="add*" parameters="**" />
+    </keep>
+
+    <!-- If we have kenv.zip, we can process the J2ME WTK plugin. -->
+
+    <keep access="public" name="proguard.wtk.ProGuardObfuscator" />
+
+  </proguard>
+</target>
+
+</project>
diff --git a/examples/ant/servlets.xml b/examples/ant/servlets.xml
new file mode 100644
index 0000000..8aa43d7
--- /dev/null
+++ b/examples/ant/servlets.xml
@@ -0,0 +1,74 @@
+<!-- This Ant build file illustrates how to process servlets.
+     Usage: ant -f servlets.xml -->
+
+<project name="Servlets" default="obfuscate" basedir="../..">
+
+<target name="obfuscate">
+  <taskdef resource="proguard/ant/task.properties"
+           classpath="lib/proguard.jar" />
+
+  <proguard printseeds="on">
+
+    <!-- Specify the input jars, output jars, and library jars. -->
+
+    <injar  file="in.jar" />
+    <outjar file="out.jar" />
+
+    <libraryjar file="${java.home}/lib/rt.jar" />
+
+    <!-- Keep all public servlets. -->
+
+    <keep access="public" implements="javax.servlet.Servlet" />
+
+    <!-- Preserve all annotations. -->
+
+    <keepattribute name="*Annotation*" />
+
+    <!-- Preserve all native method names and the names of their classes. -->
+
+    <keepclasseswithmembernames>
+      <method access="native" />
+    </keepclasseswithmembernames>
+    
+    <!-- Preserve a method that is required in all enumeration classes. -->
+    
+    <keepclassmembers extends="java.lang.Enum">
+      <method access="public"
+              type="**[]"
+              name="values"
+              parameters="" />
+    </keepclassmembers>
+
+    <!-- Explicitly preserve all serialization members. The Serializable
+         interface is only a marker interface, so it wouldn't save them.
+         You can comment this out if your library doesn't use serialization.
+         If your code contains serializable classes that have to be backward
+         compatible, please refer to the manual. -->
+
+    <keepclassmembers implements="java.io.Serializable">
+      <field  access    ="final"
+              type      ="long"
+              name      ="serialVersionUID" />
+      <method access    ="private"
+              type      ="void"
+              name      ="writeObject"
+              parameters="java.io.ObjectOutputStream" />
+      <method access    ="private"
+              type      ="void"
+              name      ="readObject"
+              parameters="java.io.ObjectOutputStream" />
+      <method type      ="java.lang.Object"
+              name      ="writeReplace"
+              parameters="" />
+      <method type      ="java.lang.Object"
+              name      ="readResolve"
+              parameters="" />
+    </keepclassmembers>
+
+    <!-- Your application may contain more items that need to be preserved;
+         typically classes that are dynamically created using Class.forName -->
+ 
+  </proguard>
+</target>
+
+</project>
diff --git a/examples/applets.pro b/examples/applets.pro
new file mode 100644
index 0000000..1005150
--- /dev/null
+++ b/examples/applets.pro
@@ -0,0 +1,57 @@
+#
+# This ProGuard configuration file illustrates how to process applets.
+# Usage:
+#     java -jar proguard.jar @applets.pro
+#
+
+# Specify the input jars, output jars, and library jars.
+
+-injars  in.jar
+-outjars out.jar
+
+-libraryjars <java.home>/lib/rt.jar
+
+# Preserve all public applets.
+
+-keep public class * extends java.applet.Applet
+
+# Print out a list of what we're preserving.
+
+-printseeds
+
+# Preserve all annotations.
+
+-keepattributes *Annotation*
+
+# Preserve all native method names and the names of their classes.
+
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+# Preserve a method that is required in all enumeration classes.
+
+-keepclassmembers class * extends java.lang.Enum {
+    public **[] values();
+}
+
+# Explicitly preserve all serialization members. The Serializable interface
+# is only a marker interface, so it wouldn't save them.
+# You can comment this out if your library doesn't use serialization.
+# If your code contains serializable classes that have to be backward
+# compatible, please refer to the manual.
+
+-keepclassmembers class * implements java.io.Serializable {
+    static final long serialVersionUID;
+    private void writeObject(java.io.ObjectOutputStream);
+    private void readObject(java.io.ObjectInputStream);
+    java.lang.Object writeReplace();
+    java.lang.Object readResolve();
+}
+
+# Your application may contain more items that need to be preserved;
+# typically classes that are dynamically created using Class.forName:
+
+# -keep public class mypackage.MyClass
+# -keep public interface mypackage.MyInterface
+# -keep public class * implements mypackage.MyInterface
diff --git a/examples/applications.pro b/examples/applications.pro
new file mode 100644
index 0000000..1adf7b2
--- /dev/null
+++ b/examples/applications.pro
@@ -0,0 +1,63 @@
+#
+# This ProGuard configuration file illustrates how to process applications.
+# Usage:
+#     java -jar proguard.jar @applications.pro
+#
+
+# Specify the input jars, output jars, and library jars.
+
+-injars  in.jar
+-outjars out.jar
+
+-libraryjars <java.home>/lib/rt.jar
+#-libraryjars junit.jar
+#-libraryjars servlet.jar
+#-libraryjars jai_core.jar
+#...
+
+# Preserve all public applications.
+
+-keepclasseswithmembers public class * {
+    public static void main(java.lang.String[]);
+}
+
+# Print out a list of what we're preserving.
+
+-printseeds
+
+# Preserve all annotations.
+
+-keepattributes *Annotation*
+
+# Preserve all native method names and the names of their classes.
+
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+# Preserve a method that is required in all enumeration classes.
+
+-keepclassmembers class * extends java.lang.Enum {
+    public **[] values();
+}
+
+# Explicitly preserve all serialization members. The Serializable interface
+# is only a marker interface, so it wouldn't save them.
+# You can comment this out if your application doesn't use serialization.
+# If your code contains serializable classes that have to be backward 
+# compatible, please refer to the manual.
+
+-keepclassmembers class * implements java.io.Serializable {
+    static final long serialVersionUID;
+    private void writeObject(java.io.ObjectOutputStream);
+    private void readObject(java.io.ObjectInputStream);
+    java.lang.Object writeReplace();
+    java.lang.Object readResolve();
+}
+
+# Your application may contain more items that need to be preserved; 
+# typically classes that are dynamically created using Class.forName:
+
+# -keep public class mypackage.MyClass
+# -keep public interface mypackage.MyInterface
+# -keep public class * implements mypackage.MyInterface
diff --git a/examples/dictionaries/compact.txt b/examples/dictionaries/compact.txt
new file mode 100644
index 0000000..c97bdd3
--- /dev/null
+++ b/examples/dictionaries/compact.txt
@@ -0,0 +1,19 @@
+#
+# This obfuscation dictionary contains strings that are already present
+# in many class files. Since these strings can be shared, the resulting
+# obfuscated class files will generally be a little bit more compact.
+# Usage:
+#     java -jar proguard.jar ..... -obfuscationdictionary compact.txt
+#
+
+Code
+Exceptions
+V
+I
+Z
+B
+C
+S
+F
+D
+L
diff --git a/examples/dictionaries/keywords.txt b/examples/dictionaries/keywords.txt
new file mode 100644
index 0000000..76f5a7b
--- /dev/null
+++ b/examples/dictionaries/keywords.txt
@@ -0,0 +1,58 @@
+#
+# This obfuscation dictionary contains reserved Java keywords. They can't
+# be used in Java source files, but they can be used in compiled class files.
+# Note that this hardly improves the obfuscation. Decent decompilers can
+# automatically replace reserved keywords, and the effect can fairly simply be
+# undone by obfuscating again with simpler names.
+# Usage:
+#     java -jar proguard.jar ..... -obfuscationdictionary keywords.txt
+#
+
+do
+if
+for
+int
+new
+try
+byte
+case
+char
+else
+goto
+long
+this
+void
+break
+catch
+class
+const
+final
+float
+short
+super
+throw
+while
+double
+import
+native
+public
+return
+static
+switch
+throws
+boolean
+default
+extends
+finally
+package
+private
+abstract
+continue
+strictfp
+volatile
+interface
+protected
+transient
+implements
+instanceof
+synchronized
diff --git a/examples/dictionaries/shakespeare.txt b/examples/dictionaries/shakespeare.txt
new file mode 100644
index 0000000..28b1cd8
--- /dev/null
+++ b/examples/dictionaries/shakespeare.txt
@@ -0,0 +1,23 @@
+#
+# This obfuscation dictionary contains quotes from plays by Shakespeare.
+# It illustrates that any text can be used, for whatever flippant reasons
+# one may have.
+# Usage:
+#     java -jar proguard.jar ..... -obfuscationdictionary shakespeare.txt
+#
+
+
+"This thing of darkness I acknowledge mine."
+
+  --From The Tempest (V, i, 275-276) 
+
+
+"Though this be madness, yet there is method in 't."
+
+  --From Hamlet (II, ii, 206)
+
+
+"What's in a name? That which we call a rose
+ By any other word would smell as sweet."
+
+  --From Romeo and Juliet (II, ii, 1-2)
diff --git a/examples/library.pro b/examples/library.pro
new file mode 100644
index 0000000..1a1a1fa
--- /dev/null
+++ b/examples/library.pro
@@ -0,0 +1,74 @@
+#
+# This ProGuard configuration file illustrates how to process a program
+# library, such that it remains usable as a library.
+# Usage:
+#     java -jar proguard.jar @library.pro
+#
+
+# Specify the input jars, output jars, and library jars.
+# In this case, the input jar is the program library that we want to process.
+
+-injars  in.jar
+-outjars out.jar
+
+-libraryjars  <java.home>/lib/rt.jar
+
+# Save the obfuscation mapping to a file, so we can de-obfuscate any stack
+# traces later on. Keep a fixed source file attribute and all line number
+# tables to actually get these stack traces.
+# You can comment this out if you're not interested in stack traces.
+
+-printmapping out.map
+-renamesourcefileattribute SourceFile
+-keepattributes InnerClasses,SourceFile,LineNumberTable,Deprecated
+
+# Preserve all annotations.
+
+-keepattributes *Annotation*
+
+# Preserve all public classes, and their public and protected fields and
+# methods.
+
+-keep public class * {
+    public protected *;
+}
+
+# Preserve all .class method names.
+
+-keepclassmembernames class * {
+    java.lang.Class class$(java.lang.String);
+    java.lang.Class class$(java.lang.String, boolean);
+}
+
+# Preserve all native method names and the names of their classes.
+
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+# Preserve a method that is required in all enumeration classes.
+
+-keepclassmembers class * extends java.lang.Enum {
+    public **[] values();
+}
+
+# Explicitly preserve all serialization members. The Serializable interface
+# is only a marker interface, so it wouldn't save them.
+# You can comment this out if your library doesn't use serialization.
+# If your code contains serializable classes that have to be backward
+# compatible, please refer to the manual.
+
+-keepclassmembers class * implements java.io.Serializable {
+    static final long serialVersionUID;
+    private void writeObject(java.io.ObjectOutputStream);
+    private void readObject(java.io.ObjectInputStream);
+    java.lang.Object writeReplace();
+    java.lang.Object readResolve();
+}
+
+# Your library may contain more items that need to be preserved; 
+# typically classes that are dynamically created using Class.forName:
+
+# -keep public class mypackage.MyClass
+# -keep public interface mypackage.MyInterface
+# -keep public class * implements mypackage.MyInterface
diff --git a/examples/midlets.pro b/examples/midlets.pro
new file mode 100644
index 0000000..5ffb85b
--- /dev/null
+++ b/examples/midlets.pro
@@ -0,0 +1,53 @@
+#
+# This ProGuard configuration file illustrates how to process J2ME midlets.
+# Usage:
+#     java -jar proguard.jar @midlets.pro
+#
+# You should still apply the preverify tool after having processed your code.
+
+# Specify the input jars, output jars, and library jars.
+
+-injars  in.jar
+-outjars out.jar
+
+-libraryjars /usr/local/java/wtk2.1/lib/midpapi20.jar
+-libraryjars /usr/local/java/wtk2.1/lib/cldcapi11.jar
+
+# Allow methods with the same signature, except for the return type,
+# to get the same obfuscation name.
+
+-overloadaggressively
+
+# Put all obfuscated classes into the nameless root package.
+
+-defaultpackage ''
+
+# Allow classes and class members to be made public.
+
+-allowaccessmodification
+
+# On Windows, you can't use mixed case class names,
+# for the sake of the preverify tool.
+#
+# -dontusemixedcaseclassnames
+
+# Preserve all public midlets.
+
+-keep public class * extends javax.microedition.midlet.MIDlet
+
+# Print out a list of what we're preserving.
+
+-printseeds
+
+# Preserve all native method names and the names of their classes.
+
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+# Your midlet may contain more items that need to be preserved; 
+# typically classes that are dynamically created using Class.forName:
+
+# -keep public class mypackage.MyClass
+# -keep public interface mypackage.MyInterface
+# -keep public class * implements mypackage.MyInterface
diff --git a/examples/proguard.pro b/examples/proguard.pro
new file mode 100644
index 0000000..1a4a7b7
--- /dev/null
+++ b/examples/proguard.pro
@@ -0,0 +1,55 @@
+#
+# This ProGuard configuration file illustrates how to process ProGuard itself.
+# Configuration files for typical applications will be very similar.
+# Usage:
+#     java -jar proguard.jar @proguard.pro
+#
+
+# Specify the input jars, output jars, and library jars.
+# We'll filter out the Ant and WTK classes, keeping everything else.
+
+-injars  proguard.jar(!proguard/ant/**,!proguard/wtk/**)
+-outjars proguard_out.jar
+
+-libraryjars <java.home>/lib/rt.jar
+
+# Write out an obfuscation mapping file, for de-obfuscating any stack traces
+# later on, or for incremental obfuscation of extensions.
+
+-printmapping proguard.map
+
+# Allow methods with the same signature, except for the return type,
+# to get the same obfuscation name.
+
+-overloadaggressively
+
+# Put all obfuscated classes into the nameless root package.
+
+-defaultpackage ''
+
+# Allow classes and class members to be made public.
+
+-allowaccessmodification
+
+# The entry point: ProGuard and its main method.
+
+-keep public class proguard.ProGuard {
+    public static void main(java.lang.String[]);
+}
+
+# If you want to preserve the Ant task as well, you'll have to specify the
+# main ant.jar.
+
+#-libraryjars /usr/local/java/ant1.6.2/lib/ant.jar
+#-keep public class proguard.ant.* {
+#    public void set*(%);
+#    public void set*(**);
+#    public void add*(**);
+#}
+
+
+# If you want to preserve the WTK obfuscation plug-in, you'll have to specify
+# the kenv.zip file.
+
+#-libraryjars /usr/local/java/wtk2.1/wtklib/kenv.zip
+#-keep public class proguard.wtk.ProGuardObfuscator
diff --git a/examples/proguardall.pro b/examples/proguardall.pro
new file mode 100644
index 0000000..b0e14e1
--- /dev/null
+++ b/examples/proguardall.pro
@@ -0,0 +1,67 @@
+#
+# This ProGuard configuration file illustrates how to process ProGuard
+# (including its main application, its GUI, its Ant task, and its WTK plugin),
+# and the ReTrace tool, all in one go.
+# Configuration files for typical applications will be very similar.
+# Usage:
+#     java -jar proguard.jar @proguardall.pro
+#
+
+# Specify the input jars, output jars, and library jars.
+# We'll read all jars from the lib directory, process them, and write the
+# processed jars to a new out directory.
+
+-injars  lib
+-outjars out
+
+# You may have to adapt the paths below.
+
+-libraryjars <java.home>/lib/rt.jar
+-libraryjars /usr/local/java/ant1.6.2/lib/ant.jar
+-libraryjars /usr/local/java/wtk2.1/wtklib/kenv.zip
+
+# Allow methods with the same signature, except for the return type,
+# to get the same obfuscation name.
+
+-overloadaggressively
+
+# Put all obfuscated classes into the nameless root package.
+
+-defaultpackage ''
+
+# Allow classes and class members to be made public.
+
+-allowaccessmodification
+
+# The main entry points.
+
+-keep public class proguard.ProGuard {
+    public static void main(java.lang.String[]);
+}
+
+-keep public class proguard.gui.ProGuardGUI {
+    public static void main(java.lang.String[]);
+}
+
+-keep public class proguard.retrace.ReTrace {
+    public static void main(java.lang.String[]);
+}
+
+# If we have ant.jar, we can properly process the Ant task.
+
+-keep public class proguard.ant.* {
+    public void set*(%);
+    public void set*(**);
+    public void add*(**);
+}
+
+# If we have kenv.zip, we can process the J2ME WTK plugin.
+
+-keep public class proguard.wtk.ProGuardObfuscator
+
+# In addition, the following classes load resource files, based on their class
+# names or package names, so we don't want them to be obfuscated or moved to 
+# the default package.
+
+-keep class proguard.gui.GUIResources
+-keep class proguard.gui.ClassPathPanel$MyListCellRenderer
diff --git a/examples/proguardgui.pro b/examples/proguardgui.pro
new file mode 100644
index 0000000..1dca7bd
--- /dev/null
+++ b/examples/proguardgui.pro
@@ -0,0 +1,48 @@
+#
+# This ProGuard configuration file illustrates how to process the ProGuard GUI.
+# Configuration files for typical applications will be very similar.
+# Usage:
+#     java -jar proguard.jar @proguardgui.pro
+#
+
+# Specify the input jars, output jars, and library jars.
+# We'll filter out the Ant and WTK classes, keeping everything else.
+
+-injars  proguard.jar(!proguard/ant/**,!proguard/wtk/**)
+-injars  proguardgui.jar
+-outjars proguardgui_out.jar
+
+-libraryjars <java.home>/lib/rt.jar
+
+# If we wanted to reuse the previously obfuscated proguard_out.jar, we could
+# perform incremental obfuscation based on its mapping file, and only keep the
+# additional GUI files instead of all files.
+
+#-applymapping proguard.map
+#-outjars      proguardgui_out.jar(proguard/gui/**)
+
+# Allow methods with the same signature, except for the return type,
+# to get the same obfuscation name.
+
+-overloadaggressively
+
+# Put all obfuscated classes into the nameless root package.
+
+-defaultpackage ''
+
+# Allow classes and class members to be made public.
+
+-allowaccessmodification
+
+# The entry point: ProGuardGUI and its main method.
+
+-keep public class proguard.gui.ProGuardGUI {
+    public static void main(java.lang.String[]);
+}
+
+# In addition, the following classes load resource files, based on their class
+# names or package names, so we don't want them to be obfuscated or moved to
+# the default package.
+
+-keep class proguard.gui.GUIResources
+-keep class proguard.gui.ClassPathPanel$MyListCellRenderer
diff --git a/examples/retrace.pro b/examples/retrace.pro
new file mode 100644
index 0000000..b9aa0f6
--- /dev/null
+++ b/examples/retrace.pro
@@ -0,0 +1,41 @@
+#
+# This ProGuard configuration file illustrates how to process the ReTrace tool.
+# Configuration files for typical applications will be very similar.
+# Usage:
+#     java -jar proguard.jar @retrace.pro
+#
+
+# Specify the input jars, output jars, and library jars.
+# We'll filter out the Ant and WTK classes, keeping everything else.
+
+-injars  proguard.jar(!proguard/ant/**,!proguard/wtk/**)
+-injars  retrace.jar
+-outjars retrace_out.jar
+
+-libraryjars <java.home>/lib/rt.jar
+
+# If we wanted to reuse the previously obfuscated proguard_out.jar, we could
+# perform incremental obfuscation based on its mapping file, and only keep the
+# additional ReTrace files instead of all files.
+
+#-applymapping proguard.map
+#-outjars      retrace_out.jar(proguard/retrace/**)
+
+# Allow methods with the same signature, except for the return type,
+# to get the same obfuscation name.
+
+-overloadaggressively
+
+# Put all obfuscated classes into the nameless root package.
+
+-defaultpackage ''
+
+# Allow classes and class members to be made public.
+
+-allowaccessmodification
+
+# The entry point: ReTrace and its main method.
+
+-keep public class proguard.retrace.ReTrace {
+    public static void main(java.lang.String[]);
+}
diff --git a/examples/servlets.pro b/examples/servlets.pro
new file mode 100644
index 0000000..3e16c8c
--- /dev/null
+++ b/examples/servlets.pro
@@ -0,0 +1,58 @@
+#
+# This ProGuard configuration file illustrates how to process servlets.
+# Usage:
+#     java -jar proguard.jar @servlets.pro
+#
+
+# Specify the input jars, output jars, and library jars.
+
+-injars  in.jar
+-outjars out.jar
+
+-libraryjars <java.home>/lib/rt.jar
+-libraryjars /usr/local/java/servlet/servlet.jar
+
+# Preserve all public servlets.
+
+-keep public class * implements javax.servlet.Servlet
+
+# Print out a list of what we're preserving.
+
+-printseeds
+
+# Preserve all annotations.
+
+-keepattributes *Annotation*
+
+# Preserve all native method names and the names of their classes.
+
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+# Preserve a method that is required in all enumeration classes.
+
+-keepclassmembers class * extends java.lang.Enum {
+    public **[] values();
+}
+
+# Explicitly preserve all serialization members. The Serializable interface
+# is only a marker interface, so it wouldn't save them.
+# You can comment this out if your library doesn't use serialization.
+# If your code contains serializable classes that have to be backward
+# compatible, please refer to the manual.
+
+-keepclassmembers class * implements java.io.Serializable {
+    static final long serialVersionUID;
+    private void writeObject(java.io.ObjectOutputStream);
+    private void readObject(java.io.ObjectInputStream);
+    java.lang.Object writeReplace();
+    java.lang.Object readResolve();
+}
+
+# Your application may contain more items that need to be preserved;
+# typically classes that are dynamically created using Class.forName:
+
+# -keep public class mypackage.MyClass
+# -keep public interface mypackage.MyInterface
+# -keep public class * implements mypackage.MyInterface
diff --git a/src/proguard/ArgumentWordReader.java b/src/proguard/ArgumentWordReader.java
new file mode 100644
index 0000000..7494205
--- /dev/null
+++ b/src/proguard/ArgumentWordReader.java
@@ -0,0 +1,83 @@
+/* $Id: ArgumentWordReader.java,v 1.11 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+import java.io.*;
+
+
+/**
+ * A <code>WordReader</code> that returns words from an argument list.
+ * Single arguments are split into individual words if necessary.
+ *
+ * @author Eric Lafortune
+ */
+public class ArgumentWordReader extends WordReader
+{
+    private String[] arguments;
+    private int      index = 0;
+
+
+    /**
+     * Creates a new ArgumentWordReader for the given arguments.
+     */
+    public ArgumentWordReader(String[] arguments)
+    {
+        this.arguments = arguments;
+    }
+
+
+    // Implementations for WordReader.
+
+    protected String nextLine() throws IOException
+    {
+        return index < arguments.length ?
+            arguments[index++] :
+            null;
+    }
+
+
+    protected String lineLocationDescription()
+    {
+        return "argument number " + index;
+    }
+
+
+    /**
+     * Test application that prints out the individual words of
+     * the argument list.
+     */
+    public static void main(String[] args) {
+        try
+        {
+            WordReader reader = new ArgumentWordReader(args);
+            while (true)
+            {
+                String word = reader.nextWord();
+                if (word == null)
+                    System.exit(-1);
+
+                System.err.println("["+word+"]");
+            }
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/src/proguard/ClassMemberSpecification.java b/src/proguard/ClassMemberSpecification.java
new file mode 100644
index 0000000..45cfab1
--- /dev/null
+++ b/src/proguard/ClassMemberSpecification.java
@@ -0,0 +1,103 @@
+/* $Id: ClassMemberSpecification.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+
+/**
+ * This class stores a specification of class members. The specification is
+ * template-based: the class member names and descriptors can contain wildcards.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassMemberSpecification
+{
+    public int    requiredSetAccessFlags;
+    public int    requiredUnsetAccessFlags;
+    public String name;
+    public String descriptor;
+
+
+    /**
+     * Creates a new option to keep the specified class member(s).
+     */
+    public ClassMemberSpecification()
+    {
+        this(0,
+             0,
+             null,
+             null);
+    }
+
+
+    /**
+     * Creates a new option to keep the specified class member(s).
+     *
+     * @param requiredSetAccessFlags   the class access flags that must be set
+     *                                 in order for the class to apply.
+     * @param requiredUnsetAccessFlags the class access flags that must be unset
+     *                                 in order for the class to apply.
+     * @param name                     the class member name. The name may be
+     *                                 null to specify any class member or it
+     *                                 may contain "*" or "?" wildcards.
+     * @param descriptor               the class member descriptor. The
+     *                                 descriptor may be null to specify any
+     *                                 class member or it may contain
+     *                                 "**", "*", or "?" wildcards.
+     */
+    public ClassMemberSpecification(int    requiredSetAccessFlags,
+                                 int    requiredUnsetAccessFlags,
+                                 String name,
+                                 String descriptor)
+    {
+        this.requiredSetAccessFlags   = requiredSetAccessFlags;
+        this.requiredUnsetAccessFlags = requiredUnsetAccessFlags;
+        this.name                     = name;
+        this.descriptor               = descriptor;
+    }
+
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (this.getClass() != object.getClass())
+        {
+            return false;
+        }
+
+        ClassMemberSpecification other = (ClassMemberSpecification)object;
+        return
+            (this.requiredSetAccessFlags   == other.requiredSetAccessFlags  ) &&
+            (this.requiredUnsetAccessFlags == other.requiredUnsetAccessFlags) &&
+            (this.name       == null ? other.name       == null : this.name.equals(other.name)            ) &&
+            (this.descriptor == null ? other.descriptor == null : this.descriptor.equals(other.descriptor));
+    }
+
+    public int hashCode()
+    {
+        return
+            requiredSetAccessFlags                           ^
+            requiredUnsetAccessFlags                         ^
+            (name       == null ? 0 : name.hashCode()      ) ^
+            (descriptor == null ? 0 : descriptor.hashCode());
+    }
+}
diff --git a/src/proguard/ClassPath.java b/src/proguard/ClassPath.java
new file mode 100644
index 0000000..4df62c6
--- /dev/null
+++ b/src/proguard/ClassPath.java
@@ -0,0 +1,94 @@
+/* $Id: ClassPath.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+import java.util.*;
+
+
+/**
+ * This class represents a class path, as a list of ClassPathEntry objects.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassPath
+{
+    private List classPathEntries = new ArrayList();
+
+
+    /**
+     * Returns whether the class path contains any output entries.
+     */
+    public boolean hasOutput()
+    {
+        for (int index = 0; index < classPathEntries.size(); index++)
+        {
+            if (((ClassPathEntry)classPathEntries.get(index)).isOutput())
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+    // Delegates to List.
+
+    public void clear()
+    {
+        classPathEntries.clear();
+    }
+
+    public void add(int index, ClassPathEntry classPathEntry)
+    {
+        classPathEntries.add(index, classPathEntry);
+    }
+
+    public boolean add(ClassPathEntry classPathEntry)
+    {
+        return classPathEntries.add(classPathEntry);
+    }
+
+    public boolean addAll(ClassPath classPath)
+    {
+        return classPathEntries.addAll(classPath.classPathEntries);
+    }
+
+    public ClassPathEntry get(int index)
+    {
+        return (ClassPathEntry)classPathEntries.get(index);
+    }
+
+    public ClassPathEntry remove(int index)
+    {
+        return (ClassPathEntry)classPathEntries.remove(index);
+    }
+
+    public boolean isEmpty()
+    {
+        return classPathEntries.isEmpty();
+    }
+
+    public int size()
+    {
+        return classPathEntries.size();
+    }
+}
diff --git a/src/proguard/ClassPathEntry.java b/src/proguard/ClassPathEntry.java
new file mode 100644
index 0000000..808efbb
--- /dev/null
+++ b/src/proguard/ClassPathEntry.java
@@ -0,0 +1,157 @@
+/* $Id: ClassPathEntry.java,v 1.8 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+
+/**
+ * This class represents an entry from a class path: a jar, a war, a zip, an
+ * ear, or a directory, with a name and a flag to indicates whether the entry is
+ * an input entry or an output entry. Optional filters can be specified for the
+ * names of the contained resource/class files, jars, wars, ears, and zips.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassPathEntry
+{
+    private String  name;
+    private boolean output;
+    private String  filter;
+    private String  jarFilter;
+    private String  warFilter;
+    private String  earFilter;
+    private String  zipFilter;
+
+
+    /**
+     * Creates a new ClassPathEntry with the given name and type.
+     */
+    public ClassPathEntry(String name, boolean isOutput)
+    {
+        this.name   = name;
+        this.output = isOutput;
+    }
+
+
+    public String getName()
+    {
+        return name;
+    }
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+
+    public boolean isOutput()
+    {
+        return output;
+    }
+
+
+    public void setOutput(boolean output)
+    {
+        this.output = output;
+    }
+
+
+    public String getFilter()
+    {
+        return filter;
+    }
+
+    public void setFilter(String filter)
+    {
+        this.filter = filter == null || filter.length() == 0 ? null : filter;
+    }
+
+
+    public String getJarFilter()
+    {
+        return jarFilter;
+    }
+
+    public void setJarFilter(String filter)
+    {
+        this.jarFilter = filter == null || filter.length() == 0 ? null : filter;
+    }
+
+
+    public String getWarFilter()
+    {
+        return warFilter;
+    }
+
+    public void setWarFilter(String filter)
+    {
+        this.warFilter = filter == null || filter.length() == 0 ? null : filter;
+    }
+
+
+    public String getEarFilter()
+    {
+        return earFilter;
+    }
+
+    public void setEarFilter(String filter)
+    {
+        this.earFilter = filter == null || filter.length() == 0 ? null : filter;
+    }
+
+
+    public String getZipFilter()
+    {
+        return zipFilter;
+    }
+
+    public void setZipFilter(String filter)
+    {
+        this.zipFilter = filter == null || filter.length() == 0 ? null : filter;
+    }
+
+
+    public String toString()
+    {
+        String string = name;
+
+        if (filter    != null ||
+            jarFilter != null ||
+            warFilter != null ||
+            earFilter != null ||
+            zipFilter != null)
+        {
+            string +=
+                ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD +
+                (zipFilter != null ? zipFilter : "")  +
+                ConfigurationConstants.SEPARATOR_KEYWORD +
+                (earFilter != null ? earFilter : "")  +
+                ConfigurationConstants.SEPARATOR_KEYWORD +
+                (warFilter != null ? warFilter : "")  +
+                ConfigurationConstants.SEPARATOR_KEYWORD +
+                (jarFilter != null ? jarFilter : "")  +
+                ConfigurationConstants.SEPARATOR_KEYWORD +
+                (filter    != null ? filter    : "")  +
+                ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD;
+        }
+
+        return string;
+    }
+}
diff --git a/src/proguard/ClassSpecification.java b/src/proguard/ClassSpecification.java
new file mode 100644
index 0000000..faeb157
--- /dev/null
+++ b/src/proguard/ClassSpecification.java
@@ -0,0 +1,224 @@
+/* $Id: ClassSpecification.java,v 1.3 2004/08/28 17:03:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+import java.util.*;
+
+
+/**
+ * This class stores a specification of classes and possibly class members.
+ * The specification is template-based: the class names and class member names
+ * and descriptors can contain wildcards. Classes can be specified explicitly,
+ * or as extensions or implementations in the class hierarchy.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassSpecification implements Cloneable
+{
+    public int     requiredSetAccessFlags;
+    public int     requiredUnsetAccessFlags;
+    public String  className;
+    public String  extendsClassName;
+    public boolean markClassFiles;
+    public boolean markConditionally;
+    public String  comments;
+
+    public List    fieldSpecifications;
+    public List    methodSpecifications;
+
+
+    /**
+     * Creates a new option to keep all possible class(es).
+     * The option doesn't have comments.
+     */
+    public ClassSpecification()
+    {
+        this(0,
+             0,
+             null,
+             null,
+             true,
+             false);
+    }
+
+
+    /**
+     * Creates a new option to keep the specified class(es).
+     * The option doesn't have comments.
+     *
+     * @param requiredSetAccessFlags   the class access flags that must be set
+     *                                 in order for the class to apply.
+     * @param requiredUnsetAccessFlags the class access flags that must be unset
+     *                                 in order for the class to apply.
+     * @param className                the class name. The name may be null to
+     *                                 specify any class, or it may contain
+     *                                 "**", "*", or "?" wildcards.
+     * @param extendsClassName         the name of the class that the class must
+     *                                 extend or implement in order to apply.
+     *                                 The name may be null to specify any class.
+     * @param markClassFiles           specifies whether to mark the class files.
+     *                                 If false, only class members are marked.
+     *                                 If true, the class files are marked as
+     *                                 well.
+     * @param markConditionally        specifies whether to mark the class files
+     *                                 and class members conditionally.
+     *                                 If true, class files and class members
+     *                                 are marked, on the condition that all
+     *                                 specified class members are present.
+     */
+    public ClassSpecification(int     requiredSetAccessFlags,
+                               int     requiredUnsetAccessFlags,
+                               String  className,
+                               String  extendsClassName,
+                               boolean markClassFiles,
+                               boolean markConditionally)
+    {
+        this(requiredSetAccessFlags,
+             requiredUnsetAccessFlags,
+             className,
+             extendsClassName,
+             markClassFiles,
+             markConditionally,
+             null);
+    }
+
+
+    /**
+     * Creates a new option to keep the specified class(es).
+     *
+     * @param requiredSetAccessFlags   the class access flags that must be set
+     *                                 in order for the class to apply.
+     * @param requiredUnsetAccessFlags the class access flags that must be unset
+     *                                 in order for the class to apply.
+     * @param className                the class name. The name may be null to
+     *                                 specify any class, or it may contain
+     *                                 "**", "*", or "?" wildcards.
+     * @param extendsClassName         the name of the class that the class must
+     *                                 extend or implement in order to apply.
+     *                                 The name may be null to specify any class.
+     * @param markClassFiles           specifies whether to mark the class files.
+     *                                 If false, only class members are marked.
+     *                                 If true, the class files are marked as
+     *                                 well.
+     * @param markConditionally        specifies whether to mark the class files
+     *                                 and class members conditionally.
+     *                                 If true, class files and class members
+     *                                 are marked, on the condition that all
+     *                                 specified class members are present.
+     * @param comments                 provides optional comments on this option.
+     */
+    public ClassSpecification(int     requiredSetAccessFlags,
+                               int     requiredUnsetAccessFlags,
+                               String  className,
+                               String  extendsClassName,
+                               boolean markClassFiles,
+                               boolean markConditionally,
+                               String  comments)
+    {
+        this.requiredSetAccessFlags   = requiredSetAccessFlags;
+        this.requiredUnsetAccessFlags = requiredUnsetAccessFlags;
+        this.className                = className;
+        this.extendsClassName         = extendsClassName;
+        this.markClassFiles           = markClassFiles;
+        this.markConditionally        = markConditionally;
+        this.comments                 = comments;
+    }
+
+
+    /**
+     * Specifies to keep the specified field(s) of this option's class(es).
+     *
+     * @param fieldSpecification the field specification.
+     */
+    public void addField(ClassMemberSpecification fieldSpecification)
+    {
+        if (fieldSpecifications == null)
+        {
+            fieldSpecifications = new ArrayList();
+        }
+
+        fieldSpecifications.add(fieldSpecification);
+    }
+
+
+    /**
+     * Specifies to keep the specified method(s) of this option's class(es).
+     *
+     * @param methodSpecification the method specification.
+     */
+    public void addMethod(ClassMemberSpecification methodSpecification)
+    {
+        if (methodSpecifications == null)
+        {
+            methodSpecifications = new ArrayList();
+        }
+
+        methodSpecifications.add(methodSpecification);
+    }
+
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (this.getClass() != object.getClass())
+        {
+            return false;
+        }
+
+        ClassSpecification other = (ClassSpecification)object;
+        return
+            (this.requiredSetAccessFlags   == other.requiredSetAccessFlags  ) &&
+            (this.requiredUnsetAccessFlags == other.requiredUnsetAccessFlags) &&
+            (this.markClassFiles           == other.markClassFiles          ) &&
+            (this.markConditionally        == other.markConditionally       ) &&
+            (this.className            == null ? other.className            == null : this.className.equals(other.className))                     &&
+            (this.extendsClassName     == null ? other.extendsClassName     == null : this.extendsClassName.equals(other.extendsClassName))       &&
+            (this.fieldSpecifications  == null ? other.fieldSpecifications  == null : this.fieldSpecifications.equals(other.fieldSpecifications)) &&
+            (this.methodSpecifications == null ? other.methodSpecifications == null : this.methodSpecifications.equals(other.methodSpecifications));
+    }
+
+    public int hashCode()
+    {
+        return
+            requiredSetAccessFlags                                               ^
+            requiredUnsetAccessFlags                                             ^
+            (className            == null ? 0 : className.hashCode()           ) ^
+            (extendsClassName     == null ? 0 : extendsClassName.hashCode()    ) ^
+            (markClassFiles               ? 0 : 1                              ) ^
+            (markConditionally            ? 0 : 2                              ) ^
+            (fieldSpecifications  == null ? 0 : fieldSpecifications.hashCode() ) ^
+            (methodSpecifications == null ? 0 : methodSpecifications.hashCode());
+    }
+
+    public Object clone()
+    {
+        try
+        {
+            return super.clone();
+        }
+        catch (CloneNotSupportedException e)
+        {
+            return null;
+        }
+    }
+}
diff --git a/src/proguard/ClassSpecificationVisitorFactory.java b/src/proguard/ClassSpecificationVisitorFactory.java
new file mode 100644
index 0000000..5094a99
--- /dev/null
+++ b/src/proguard/ClassSpecificationVisitorFactory.java
@@ -0,0 +1,358 @@
+/* $Id: ClassSpecificationVisitorFactory.java,v 1.5 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+import proguard.classfile.visitor.*;
+
+import java.util.*;
+
+
+/**
+ * This factory creates visitors to efficiently travel to specified classes and
+ * class members.
+ *
+ * @author Eric Lafortune
+ */
+class ClassSpecificationVisitorFactory
+{
+    /**
+     * Creates a new ClassPoolVisitor to efficiently travel to the specified
+     * classes and class members.
+     *
+     * @param classSpecifications the specifications of the classes and class
+     *                            members to visit.
+     * @param classFileVisitor    the ClassFileVisitor to be applied to matching
+     *                            classes.
+     * @param memberInfoVisitor   the MemberInfoVisitor to be applied to matching
+     *                            class members.
+     */
+    public static ClassPoolVisitor createClassPoolVisitor(List              classSpecifications,
+                                                          ClassFileVisitor  classFileVisitor,
+                                                          MemberInfoVisitor memberInfoVisitor)
+    {
+        MultiClassPoolVisitor multiClassPoolVisitor = new MultiClassPoolVisitor();
+
+        if (classSpecifications != null)
+        {
+            addClassPoolVisitors(classSpecifications,
+                                 classFileVisitor,
+                                 memberInfoVisitor,
+                                 multiClassPoolVisitor);
+        }
+
+        return multiClassPoolVisitor;
+    }
+
+
+    /**
+     * Adds new ClassPoolVisitor instances to the given MultiClassPoolVisitor,
+     * to efficiently travel to the specified classes and class members.
+
+     * @param classSpecifications   the specifications of the classes and class
+     *                              members to visit.
+     * @param classFileVisitor      the ClassFileVisitor to be applied to matching
+     *                              classes.
+     * @param memberInfoVisitor     the MemberInfoVisitor to be applied to matching
+     *                              class members.
+     * @param multiClassPoolVisitor the MultiClassPoolVisitor to which the new
+     *                              visitors will be added.
+     */
+    private static void addClassPoolVisitors(List                  classSpecifications,
+                                             ClassFileVisitor      classFileVisitor,
+                                             MemberInfoVisitor     memberInfoVisitor,
+                                             MultiClassPoolVisitor multiClassPoolVisitor)
+    {
+        for (int index = 0; index < classSpecifications.size(); index++)
+        {
+            multiClassPoolVisitor.addClassPoolVisitor(
+                createClassPoolVisitor((ClassSpecification)classSpecifications.get(index),
+                                       classFileVisitor,
+                                       memberInfoVisitor));
+        }
+    }
+
+
+    /**
+     * Creates a new ClassPoolVisitor to efficiently travel to the specified
+     * classes and class members.
+     *
+     * @param classSpecification the specifications of the class(es) and class
+     *                           members to visit.
+     * @param classFileVisitor   the ClassFileVisitor to be applied to matching
+     *                           classes.
+     * @param memberInfoVisitor  the MemberInfoVisitor to be applied to matching
+     *                           class members.
+     */
+    private static ClassPoolVisitor createClassPoolVisitor(ClassSpecification classSpecification,
+                                                           ClassFileVisitor   classFileVisitor,
+                                                           MemberInfoVisitor  memberInfoVisitor)
+    {
+        // The class file visitor for class files and their members.
+        MultiClassFileVisitor multiClassFileVisitor = new MultiClassFileVisitor();
+
+        // If specified, let the class file visitor visit the class file itself.
+        if ((classSpecification.markClassFiles ||
+             classSpecification.markConditionally) &&
+            classFileVisitor != null)
+        {
+            multiClassFileVisitor.addClassFileVisitor(classFileVisitor);
+        }
+
+        // If specified, let the member info visitor visit the class members.
+        if ((classSpecification.fieldSpecifications  != null ||
+             classSpecification.methodSpecifications != null) &&
+            memberInfoVisitor != null)
+        {
+            multiClassFileVisitor.addClassFileVisitor(
+                createClassFileVisitor(classSpecification, memberInfoVisitor));
+        }
+
+        // This visitor is the starting point.
+        ClassFileVisitor composedClassFileVisitor = multiClassFileVisitor;
+
+        // If specified, let the marker visit the class file and its class
+        // members conditionally.
+        if (classSpecification.markConditionally)
+        {
+            composedClassFileVisitor =
+                createClassFileMemberInfoTester(classSpecification,
+                                                composedClassFileVisitor);
+        }
+
+        // By default, start visiting from the class name, if it's specified.
+        String className = classSpecification.className;
+
+        // If wildcarded, only visit class files with matching names.
+        if (className != null &&
+            containsWildCards(className))
+        {
+            composedClassFileVisitor =
+                new ClassFileNameFilter(composedClassFileVisitor,
+                                        className);
+
+            // We'll have to visit all classes now.
+            className = null;
+        }
+
+        // If specified, only visit class files with the right access flags.
+        if (classSpecification.requiredSetAccessFlags   != 0 ||
+            classSpecification.requiredUnsetAccessFlags != 0)
+        {
+            composedClassFileVisitor =
+                new ClassFileAccessFilter(classSpecification.requiredSetAccessFlags,
+                                          classSpecification.requiredUnsetAccessFlags,
+                                          composedClassFileVisitor);
+        }
+
+        // If it's specified, start visiting from the extended class.
+        String extendsClassName = classSpecification.extendsClassName;
+
+        if (className        == null &&
+            extendsClassName != null)
+        {
+            composedClassFileVisitor =
+                new ClassFileHierarchyTraveler(false, false, false, true,
+                                               composedClassFileVisitor);
+
+            // If wildcarded, only visit class files with matching names.
+            if (containsWildCards(extendsClassName))
+            {
+                composedClassFileVisitor =
+                    new ClassFileNameFilter(composedClassFileVisitor,
+                                            extendsClassName);
+            }
+            else
+            {
+                // Start visiting from the extended class name.
+                className = extendsClassName;
+            }
+        }
+
+        // If specified, visit a single named class, otherwise visit all classes.
+        return className != null ?
+            (ClassPoolVisitor)new NamedClassFileVisitor(composedClassFileVisitor, className) :
+            (ClassPoolVisitor)new AllClassFileVisitor(composedClassFileVisitor);
+    }
+
+
+    /**
+     * Creates a new ClassPoolVisitor to efficiently travel to the specified class
+     * members.
+     *
+     * @param classSpecification the specifications of the class members to visit.
+     * @param memberInfoVisitor   the MemberInfoVisitor to be applied to matching
+     *                            class members.
+     */
+    private static ClassFileVisitor createClassFileVisitor(ClassSpecification classSpecification,
+                                                           MemberInfoVisitor  memberInfoVisitor)
+    {
+        MultiClassFileVisitor multiClassFileVisitor = new MultiClassFileVisitor();
+
+        addMemberInfoVisitors(classSpecification.fieldSpecifications,  true,  multiClassFileVisitor, memberInfoVisitor);
+        addMemberInfoVisitors(classSpecification.methodSpecifications, false, multiClassFileVisitor, memberInfoVisitor);
+
+        // Mark the class member in this class and in super classes.
+        return new ClassFileHierarchyTraveler(true, true, false, false,
+                                              multiClassFileVisitor);
+    }
+
+
+    /**
+     * Adds elements to the given MultiClassFileVisitor, to apply the given
+     * MemberInfoVisitor to all class members that match the given List
+     * of options (of the given type).
+     */
+    private static void addMemberInfoVisitors(List                  classMemberSpecifications,
+                                              boolean               isField,
+                                              MultiClassFileVisitor multiClassFileVisitor,
+                                              MemberInfoVisitor     memberInfoVisitor)
+    {
+        if (classMemberSpecifications != null)
+        {
+            for (int index = 0; index < classMemberSpecifications.size(); index++)
+            {
+                ClassMemberSpecification classMemberSpecification =
+                    (ClassMemberSpecification)classMemberSpecifications.get(index);
+
+                multiClassFileVisitor.addClassFileVisitor(
+                    createClassFileVisitor(classMemberSpecification,
+                                           isField,
+                                           memberInfoVisitor));
+            }
+        }
+    }
+
+
+    /**
+     * Constructs a ClassFileVisitor that conditionally applies the given
+     * ClassFileVisitor to all classes that contain the given class members.
+     */
+    private static ClassFileVisitor createClassFileMemberInfoTester(ClassSpecification classSpecification,
+                                                                    ClassFileVisitor   classFileVisitor)
+    {
+        // Create a linked list of conditional visitors, for fields and for
+        // methods.
+        return createClassFileMemberInfoTester(classSpecification.fieldSpecifications,
+                                               true,
+               createClassFileMemberInfoTester(classSpecification.methodSpecifications,
+                                               false,
+                                               classFileVisitor));
+    }
+
+
+    /**
+     * Constructs a ClassFileVisitor that conditionally applies the given
+     * ClassFileVisitor to all classes that contain the given List of class
+     * members (of the given type).
+     */
+    private static ClassFileVisitor createClassFileMemberInfoTester(List             classMemberSpecifications,
+                                                                    boolean          isField,
+                                                                    ClassFileVisitor classFileVisitor)
+    {
+        // Create a linked list of conditional visitors.
+        if (classMemberSpecifications != null)
+        {
+            for (int index = 0; index < classMemberSpecifications.size(); index++)
+            {
+                ClassMemberSpecification classMemberSpecification =
+                    (ClassMemberSpecification)classMemberSpecifications.get(index);
+
+                classFileVisitor =
+                    createClassFileVisitor(classMemberSpecification,
+                                           isField,
+                                           new ClassFileMemberInfoVisitor(classFileVisitor));
+            }
+        }
+
+        return classFileVisitor;
+    }
+
+
+    /**
+     * Creates a new ClassFileVisitor to efficiently travel to the specified class
+     * members.
+     *
+     * @param classMemberSpecification the specification of the class member(s)
+     *                                 to visit.
+     * @param memberInfoVisitor        the MemberInfoVisitor to be applied to
+     *                                 matching class member(s).
+     */
+    private static ClassFileVisitor createClassFileVisitor(ClassMemberSpecification classMemberSpecification,
+                                                           boolean                  isField,
+                                                           MemberInfoVisitor        memberInfoVisitor)
+    {
+        String name       = classMemberSpecification.name;
+        String descriptor = classMemberSpecification.descriptor;
+
+        // If name or descriptor are not fully specified, only visit matching
+        // class members.
+        boolean fullySpecified =
+            name       != null &&
+            descriptor != null &&
+            !containsWildCards(name) &&
+            !containsWildCards(descriptor);
+
+        if (!fullySpecified)
+        {
+            if (descriptor != null)
+            {
+                memberInfoVisitor =
+                    new MemberInfoDescriptorFilter(descriptor, memberInfoVisitor);
+            }
+
+            if (name != null)
+            {
+                memberInfoVisitor =
+                    new MemberInfoNameFilter(name, memberInfoVisitor);
+            }
+        }
+
+        // If any access flags are specified, only visit matching class members.
+        if (classMemberSpecification.requiredSetAccessFlags   != 0 ||
+            classMemberSpecification.requiredUnsetAccessFlags != 0)
+        {
+            memberInfoVisitor =
+                new MemberInfoAccessFilter(classMemberSpecification.requiredSetAccessFlags,
+                                           classMemberSpecification.requiredUnsetAccessFlags,
+                                           memberInfoVisitor);
+        }
+
+        // Depending on what's specified, visit a single named class member,
+        // or all class members, filtering the matching ones.
+        return isField ?
+            fullySpecified ?
+                (ClassFileVisitor)new NamedFieldVisitor(name, descriptor, memberInfoVisitor) :
+                (ClassFileVisitor)new AllFieldVisitor(memberInfoVisitor) :
+            fullySpecified ?
+                (ClassFileVisitor)new NamedMethodVisitor(name, descriptor, memberInfoVisitor) :
+                (ClassFileVisitor)new AllMethodVisitor(memberInfoVisitor);
+    }
+
+
+    // Small utility methods.
+
+    private static boolean containsWildCards(String string)
+    {
+        return string != null &&
+            (string.indexOf('*') >= 0 ||
+             string.indexOf('?') >= 0);
+    }
+}
diff --git a/src/proguard/Configuration.java b/src/proguard/Configuration.java
new file mode 100644
index 0000000..7ebe8f1
--- /dev/null
+++ b/src/proguard/Configuration.java
@@ -0,0 +1,202 @@
+/* $Id: Configuration.java,v 1.14 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+import java.util.*;
+
+
+/**
+ * The ProGuard configuration.
+ *
+ * @see ProGuard
+ *
+ * @author Eric Lafortune
+ */
+public class Configuration
+{
+    ///////////////////////////////////////////////////////////////////////////
+    // Input and output options.
+    ///////////////////////////////////////////////////////////////////////////
+
+    /**
+     * A list of input and output entries (jars, wars, ears, zips, and directories).
+     */
+    public ClassPath programJars;
+
+    /**
+     * A list of library entries (jars, wars, ears, zips, and directories).
+     */
+    public ClassPath libraryJars;
+
+    /**
+     * Specifies whether to skip non-public library classes while reading
+     * library jars.
+     */
+    public boolean   skipNonPublicLibraryClasses = true;
+
+    /**
+     * Specifies whether to skip non-public library class members while reading
+     * library classes.
+     */
+    public boolean   skipNonPublicLibraryClassMembers = true;
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Keep options.
+    ///////////////////////////////////////////////////////////////////////////
+
+    /**
+     * A list of {@link ClassSpecification} instances, whose class names and
+     * class member names are to be kept from shrinking, optimization, and
+     * obfuscation.
+     */
+    public List      keep;
+
+    /**
+     * A list of {@link ClassSpecification} instances, whose class names and
+     * class member names are to be kept from obfuscation.
+     */
+    public List      keepNames;
+
+    /**
+     * An optional output file name for listing the kept seeds.
+     * An empty string means the standard output.
+     */
+    public String    printSeeds;
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Shrinking options.
+    ///////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Specifies whether the code should be shrunk.
+     */
+    public boolean   shrink                      = true;
+
+    /**
+     * An optional output file name for listing the unused classes and class
+     * members. An empty string means the standard output.
+     */
+    public String    printUsage;
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Optimization options.
+    ///////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Specifies whether the code should be optimized.
+     */
+    public boolean   optimize                    = true;
+
+    /**
+     * A list of {@link ClassSpecification} instances, whose methods are
+     * assumed to have no side effects.
+     */
+    public List      assumeNoSideEffects;
+
+    /**
+     * Specifies whether the access of class members can be modified.
+     */
+    public boolean   allowAccessModification     = false;
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Obfuscation options.
+    ///////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Specifies whether the code should be obfuscated.
+     */
+    public boolean   obfuscate                   = true;
+
+    /**
+     * An optional output file name for listing the obfuscation mapping.
+     * An empty string means the standard output.
+     */
+    public String    printMapping;
+
+    /**
+     * An optional input file name for reading an obfuscation mapping.
+     */
+    public String    applyMapping;
+
+    /**
+     * An optional name of a file containing obfuscated class member names.
+     */
+    public String    obfuscationDictionary;
+
+    /**
+     * Specifies whether to apply aggressive name overloading on class members.
+     */
+    public boolean   overloadAggressively        = false;
+
+    /**
+     * An optional default package to which all classes whose name is obfuscated
+     * can be moved.
+     */
+    public String    defaultPackage;
+
+    /**
+     * Specifies whether to use mixed case class names.
+     */
+    public boolean   useMixedCaseClassNames      = true;
+
+    /**
+     * A list of <code>String</code>s specifying optional attributes to be kept.
+     * A <code>null</code> list means no attributes. An empty list means all
+     * attributes. The attribute names may contain "*" or "?" wildcards, and
+     * they may be preceded by the "!" negator.
+     */
+    public List      keepAttributes;
+
+    /**
+     * An optional replacement for all SourceFile attributes.
+     */
+    public String    newSourceFileAttribute;
+
+    ///////////////////////////////////////////////////////////////////////////
+    // General options.
+    ///////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Specifies whether to print verbose messages.
+     */
+    public boolean   verbose                     = false;
+
+    /**
+     * Specifies whether to print any notes.
+     */
+    public boolean   note                        = true;
+
+    /**
+     * Specifies whether to print any warnings.
+     */
+    public boolean   warn                        = true;
+
+    /**
+     * Specifies whether to ignore any warnings.
+     */
+    public boolean   ignoreWarnings              = false;
+
+    /**
+     * An optional output file name for printing out the processed code in a
+     * more or less readable form. An empty string means the standard output.
+     */
+    public String    dump;
+}
diff --git a/src/proguard/ConfigurationConstants.java b/src/proguard/ConfigurationConstants.java
new file mode 100644
index 0000000..6b13a26
--- /dev/null
+++ b/src/proguard/ConfigurationConstants.java
@@ -0,0 +1,94 @@
+/* $Id: ConfigurationConstants.java,v 1.10 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+/**
+ * This class provides constants for parsing and writing ProGuard configurations.
+ *
+ * @author Eric Lafortune
+ */
+class ConfigurationConstants
+{
+    public static final String OPTION_PREFIX                                     = "-";
+    public static final String AT_DIRECTIVE                                      = "@";
+    public static final String INCLUDE_DIRECTIVE                                 = "-include";
+
+    public static final String INJARS_OPTION                                     = "-injars";
+    public static final String OUTJARS_OPTION                                    = "-outjars";
+    public static final String LIBRARYJARS_OPTION                                = "-libraryjars";
+    public static final String RESOURCEJARS_OPTION                               = "-resourcejars";
+
+    public static final String KEEP_OPTION                                       = "-keep";
+    public static final String KEEP_CLASS_MEMBERS_OPTION                         = "-keepclassmembers";
+    public static final String KEEP_CLASSES_WITH_MEMBERS_OPTION                  = "-keepclasseswithmembers";
+    public static final String KEEP_NAMES_OPTION                                 = "-keepnames";
+    public static final String KEEP_CLASS_MEMBER_NAMES_OPTION                    = "-keepclassmembernames";
+    public static final String KEEP_CLASSES_WITH_MEMBER_NAMES_OPTION             = "-keepclasseswithmembernames";
+    public static final String PRINT_SEEDS_OPTION                                = "-printseeds";
+
+    public static final String DONT_SHRINK_OPTION                                = "-dontshrink";
+    public static final String PRINT_USAGE_OPTION                                = "-printusage";
+
+    public static final String DONT_OPTIMIZE_OPTION                              = "-dontoptimize";
+    public static final String ASSUME_NO_SIDE_EFFECTS_OPTION                     = "-assumenosideeffects";
+    public static final String ALLOW_ACCESS_MODIFICATION_OPTION                  = "-allowaccessmodification";
+
+    public static final String DONT_OBFUSCATE_OPTION                             = "-dontobfuscate";
+    public static final String PRINT_MAPPING_OPTION                              = "-printmapping";
+    public static final String APPLY_MAPPING_OPTION                              = "-applymapping";
+    public static final String OBFUSCATION_DICTIONARY_OPTION                     = "-obfuscationdictionary";
+    public static final String OVERLOAD_AGGRESSIVELY_OPTION                      = "-overloadaggressively";
+    public static final String DEFAULT_PACKAGE_OPTION                            = "-defaultpackage";
+    public static final String DONT_USE_MIXED_CASE_CLASS_NAMES_OPTION            = "-dontusemixedcaseclassnames";
+    public static final String KEEP_ATTRIBUTES_OPTION                            = "-keepattributes";
+    public static final String RENAME_SOURCE_FILE_ATTRIBUTE_OPTION               = "-renamesourcefileattribute";
+
+    public static final String VERBOSE_OPTION                                    = "-verbose";
+    public static final String DONT_NOTE_OPTION                                  = "-dontnote";
+    public static final String DONT_WARN_OPTION                                  = "-dontwarn";
+    public static final String IGNORE_WARNINGS_OPTION                            = "-ignorewarnings";
+    public static final String DUMP_OPTION                                       = "-dump";
+    public static final String DONT_SKIP_NON_PUBLIC_LIBRARY_CLASSES_OPTION       = "-dontskipnonpubliclibraryclasses";
+    public static final String DONT_SKIP_NON_PUBLIC_LIBRARY_CLASS_MEMBERS_OPTION = "-dontskipnonpubliclibraryclassmembers";
+
+    public static final String ANY_ATTRIBUTE_KEYWORD       = "*";
+    public static final String ATTRIBUTE_SEPARATOR_KEYWORD = ",";
+
+    public static final String JAR_SEPARATOR_KEYWORD   = System.getProperty("path.separator");
+
+    public static final char   OPEN_SYSTEM_PROPERTY    = '<';
+    public static final char   CLOSE_SYSTEM_PROPERTY   = '>';
+
+    public static final String NEGATOR_KEYWORD         = "!";
+    public static final String CLASS_KEYWORD           = "class";
+    public static final String ANY_CLASS_KEYWORD       = "*";
+    public static final String IMPLEMENTS_KEYWORD      = "implements";
+    public static final String EXTENDS_KEYWORD         = "extends";
+    public static final String OPEN_KEYWORD            = "{";
+    public static final String ANY_CLASS_MEMBER_KEYWORD  = "*";
+    public static final String ANY_FIELD_KEYWORD       = "<fields>";
+    public static final String ANY_METHOD_KEYWORD      = "<methods>";
+    public static final String OPEN_ARGUMENTS_KEYWORD  = "(";
+    public static final String ARGUMENT_SEPARATOR_KEYWORD = ",";
+    public static final String CLOSE_ARGUMENTS_KEYWORD = ")";
+    public static final String SEPARATOR_KEYWORD       = ";";
+    public static final String CLOSE_KEYWORD           = "}";
+}
diff --git a/src/proguard/ConfigurationParser.java b/src/proguard/ConfigurationParser.java
new file mode 100644
index 0000000..63700e7
--- /dev/null
+++ b/src/proguard/ConfigurationParser.java
@@ -0,0 +1,919 @@
+/* $Id: ConfigurationParser.java,v 1.19 2004/12/18 20:22:13 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+import proguard.classfile.*;
+import proguard.classfile.util.*;
+import proguard.util.*;
+
+import java.io.*;
+import java.net.URL;
+import java.util.*;
+
+
+/**
+ * This class parses ProGuard configurations. Configurations can be read from an
+ * array of arguments or from a configuration file or URL.
+ *
+ * @author Eric Lafortune
+ */
+public class ConfigurationParser
+{
+    private WordReader reader;
+    private String     nextWord;
+    private String     lastComments;
+
+
+    /**
+     * Creates a new ConfigurationParser for the given String arguments.
+     */
+    public ConfigurationParser(String[] args) throws IOException
+    {
+        reader = new ArgumentWordReader(args);
+        readNextWord();
+    }
+
+
+    /**
+     * Creates a new ConfigurationParser for the given file name.
+     */
+    public ConfigurationParser(String fileName) throws IOException
+    {
+        reader = new FileWordReader(fileName);
+        readNextWord();
+    }
+
+
+    /**
+     * Creates a new ConfigurationParser for the given URL.
+     */
+    public ConfigurationParser(URL url) throws IOException
+    {
+        reader = new FileWordReader(url);
+        readNextWord();
+    }
+
+
+    /**
+     * Parses and returns the configuration.
+     * @param configuration the configuration that is updated as a side-effect.
+     * @throws ParseException if the any of the configuration settings contains
+     *                        a syntax error.
+     * @throws IOException if an IO error occurs while reading a configuration.
+     */
+    public void parse(Configuration configuration) throws ParseException, IOException
+    {
+        while (nextWord != null)
+        {
+            lastComments = reader.lastComments();
+
+            // First include directives.
+            if      (ConfigurationConstants.AT_DIRECTIVE                                     .startsWith(nextWord) ||
+                     ConfigurationConstants.INCLUDE_DIRECTIVE                                .startsWith(nextWord)) parseIncludeArgument();
+
+            // Then configuration options with or without arguments.
+            else if (ConfigurationConstants.INJARS_OPTION                                    .startsWith(nextWord)) configuration.programJars                      = parseClassPathArgument(configuration.programJars, false);
+            else if (ConfigurationConstants.OUTJARS_OPTION                                   .startsWith(nextWord)) configuration.programJars                      = parseClassPathArgument(configuration.programJars, true);
+            else if (ConfigurationConstants.LIBRARYJARS_OPTION                               .startsWith(nextWord)) configuration.libraryJars                      = parseClassPathArgument(configuration.libraryJars, false);
+            else if (ConfigurationConstants.RESOURCEJARS_OPTION                              .startsWith(nextWord)) throw new ParseException("The '-resourcejars' option is no longer supported. Please use the '-injars' option for all input");
+            else if (ConfigurationConstants.DONT_SKIP_NON_PUBLIC_LIBRARY_CLASSES_OPTION      .startsWith(nextWord)) configuration.skipNonPublicLibraryClasses      = parseNoArgument(false);
+            else if (ConfigurationConstants.DONT_SKIP_NON_PUBLIC_LIBRARY_CLASS_MEMBERS_OPTION.startsWith(nextWord)) configuration.skipNonPublicLibraryClassMembers = parseNoArgument(false);
+
+            else if (ConfigurationConstants.KEEP_OPTION                                      .startsWith(nextWord)) configuration.keep                             = parseClassSpecificationArguments(configuration.keep, true,  false);
+            else if (ConfigurationConstants.KEEP_CLASS_MEMBERS_OPTION                        .startsWith(nextWord)) configuration.keep                             = parseClassSpecificationArguments(configuration.keep, false, false);
+            else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBERS_OPTION                 .startsWith(nextWord)) configuration.keep                             = parseClassSpecificationArguments(configuration.keep, false, true);
+            else if (ConfigurationConstants.KEEP_NAMES_OPTION                                .startsWith(nextWord)) configuration.keepNames                        = parseClassSpecificationArguments(configuration.keepNames, true,  false);
+            else if (ConfigurationConstants.KEEP_CLASS_MEMBER_NAMES_OPTION                   .startsWith(nextWord)) configuration.keepNames                        = parseClassSpecificationArguments(configuration.keepNames, false, false);
+            else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBER_NAMES_OPTION            .startsWith(nextWord)) configuration.keepNames                        = parseClassSpecificationArguments(configuration.keepNames, false, true);
+            else if (ConfigurationConstants.PRINT_SEEDS_OPTION                               .startsWith(nextWord)) configuration.printSeeds                       = parseOptionalArgument();
+
+            else if (ConfigurationConstants.DONT_SHRINK_OPTION                               .startsWith(nextWord)) configuration.shrink                           = parseNoArgument(false);
+            else if (ConfigurationConstants.PRINT_USAGE_OPTION                               .startsWith(nextWord)) configuration.printUsage                       = parseOptionalArgument();
+
+            else if (ConfigurationConstants.DONT_OPTIMIZE_OPTION                             .startsWith(nextWord)) configuration.optimize                         = parseNoArgument(false);
+            else if (ConfigurationConstants.ASSUME_NO_SIDE_EFFECTS_OPTION                    .startsWith(nextWord)) configuration.assumeNoSideEffects              = parseClassSpecificationArguments(configuration.assumeNoSideEffects, false, false);
+            else if (ConfigurationConstants.ALLOW_ACCESS_MODIFICATION_OPTION                 .startsWith(nextWord)) configuration.allowAccessModification          = parseNoArgument(true);
+
+            else if (ConfigurationConstants.DONT_OBFUSCATE_OPTION                            .startsWith(nextWord)) configuration.obfuscate                        = parseNoArgument(false);
+            else if (ConfigurationConstants.PRINT_MAPPING_OPTION                             .startsWith(nextWord)) configuration.printMapping                     = parseOptionalArgument();
+            else if (ConfigurationConstants.APPLY_MAPPING_OPTION                             .startsWith(nextWord)) configuration.applyMapping                     = parseOptionalArgument();
+            else if (ConfigurationConstants.OBFUSCATION_DICTIONARY_OPTION                    .startsWith(nextWord)) configuration.obfuscationDictionary            = parseObfuscationDictionaryArgument();
+            else if (ConfigurationConstants.OVERLOAD_AGGRESSIVELY_OPTION                     .startsWith(nextWord)) configuration.overloadAggressively             = parseNoArgument(true);
+            else if (ConfigurationConstants.DEFAULT_PACKAGE_OPTION                           .startsWith(nextWord)) configuration.defaultPackage                   = ClassUtil.internalClassName(parseOptionalArgument());
+            else if (ConfigurationConstants.DONT_USE_MIXED_CASE_CLASS_NAMES_OPTION           .startsWith(nextWord)) configuration.useMixedCaseClassNames           = parseNoArgument(false);
+            else if (ConfigurationConstants.KEEP_ATTRIBUTES_OPTION                           .startsWith(nextWord)) configuration.keepAttributes                   = parseKeepAttributesArguments(configuration.keepAttributes);
+            else if (ConfigurationConstants.RENAME_SOURCE_FILE_ATTRIBUTE_OPTION              .startsWith(nextWord)) configuration.newSourceFileAttribute           = parseOptionalArgument();
+
+            else if (ConfigurationConstants.VERBOSE_OPTION                                   .startsWith(nextWord)) configuration.verbose                          = parseNoArgument(true);
+            else if (ConfigurationConstants.DONT_NOTE_OPTION                                 .startsWith(nextWord)) configuration.note                             = parseNoArgument(false);
+            else if (ConfigurationConstants.DONT_WARN_OPTION                                 .startsWith(nextWord)) configuration.warn                             = parseNoArgument(false);
+            else if (ConfigurationConstants.IGNORE_WARNINGS_OPTION                           .startsWith(nextWord)) configuration.ignoreWarnings                   = parseNoArgument(true);
+            else if (ConfigurationConstants.DUMP_OPTION                                      .startsWith(nextWord)) configuration.dump                             = parseOptionalArgument();
+            else
+            {
+                throw new ParseException("Unknown configuration " + reader.locationDescription());
+            }
+        }
+    }
+
+
+    private void parseIncludeArgument()
+    throws ParseException, IOException
+    {
+        // Read the configuation file name.
+        readNextWord("configuration file name");
+
+        reader.includeWordReader(new FileWordReader(replaceSystemProperties(nextWord)));
+
+        readNextWord();
+    }
+
+
+    private ClassPath parseClassPathArgument(ClassPath classPath,
+                                             boolean   isOutput)
+    throws ParseException, IOException
+    {
+        // Create a new List if necessary.
+        if (classPath == null)
+        {
+            classPath = new ClassPath();
+        }
+
+        while (true)
+        {
+            // Read the next jar name.
+            readNextWord("jar or directory name");
+
+            // Create a new class path entry.
+            ClassPathEntry entry = new ClassPathEntry(replaceSystemProperties(nextWord),
+                                                      isOutput);
+
+            // Read the opening parenthesis or the separator, if any.
+            readNextWord();
+
+            // Read the optional filters.
+            if (!configurationEnd() &&
+                ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(nextWord))
+            {
+                // Read all filters in an array.
+                String[] filters = new String[5];
+
+                int counter = 0;
+                do
+                {
+                    // Read the filter.
+                    filters[counter++] = ListUtil.commaSeparatedString(parseCommaSeparatedList(false, true));
+                }
+                while (counter < filters.length &&
+                       ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord));
+
+                // Make sure there is a closing parenthesis.
+                if (!ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD.equals(nextWord))
+                {
+                    throw new ParseException("Expecting separating '" + ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD +
+                                             "' or '" + ConfigurationConstants.SEPARATOR_KEYWORD +
+                                             "', or closing '" + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD +
+                                             "' before " + reader.locationDescription());
+                }
+
+                // Set all filters from the array on the entry.
+                entry.setFilter(filters[--counter]);
+                if (counter > 0)
+                {
+                    entry.setJarFilter(filters[--counter]);
+                    if (counter > 0)
+                    {
+                        entry.setWarFilter(filters[--counter]);
+                        if (counter > 0)
+                        {
+                            entry.setEarFilter(filters[--counter]);
+                            if (counter > 0)
+                            {
+                                entry.setZipFilter(filters[--counter]);
+                            }
+                        }
+                    }
+                }
+
+                // Read the separator, if any.
+                readNextWord();
+            }
+
+            // Add the entry to the list.
+            classPath.add(entry);
+
+            if (configurationEnd())
+            {
+                return classPath;
+            }
+
+            if (!nextWord.equals(ConfigurationConstants.JAR_SEPARATOR_KEYWORD))
+            {
+                throw new ParseException("Expecting class path separator '" + ConfigurationConstants.JAR_SEPARATOR_KEYWORD +
+                                         "' before " + reader.locationDescription());
+            }
+        }
+    }
+
+
+    private List parseKeepAttributesArguments(List keepAttributes)
+    throws ParseException, IOException
+    {
+        // Create a new List if necessary.
+        if (keepAttributes == null)
+        {
+            keepAttributes = new ArrayList();
+        }
+
+        // Read the first attribute name.
+        readNextWord();
+
+        // Should we keep all attributes?
+        if (configurationEnd())
+        {
+            keepAttributes.clear();
+            return keepAttributes;
+        }
+
+        if (nextWord.equals(ConfigurationConstants.ANY_ATTRIBUTE_KEYWORD))
+        {
+            keepAttributes.clear();
+            readNextWord();
+            return keepAttributes;
+        }
+
+        while (true)
+        {
+            // Add the attribute name to the list.
+            keepAttributes.add(nextWord);
+
+            // Read the separator, if any.
+            readNextWord();
+            if (configurationEnd())
+            {
+                break;
+            }
+
+            if (!nextWord.equals(ConfigurationConstants.ATTRIBUTE_SEPARATOR_KEYWORD))
+            {
+                throw new ParseException("Expecting attribute name separator '" + ConfigurationConstants.ATTRIBUTE_SEPARATOR_KEYWORD +
+                                         "' before " + reader.locationDescription());
+            }
+
+            // Read the next attribute name.
+            readNextWord("attribute name");
+        }
+
+        return keepAttributes;
+    }
+
+
+    private String parseObfuscationDictionaryArgument()
+    throws ParseException, IOException
+    {
+        // Read the obfsucation dictionary name.
+        readNextWord("obfuscation dictionary name");
+
+        String obfuscationDictionary = replaceSystemProperties(nextWord);
+
+        readNextWord();
+
+        return obfuscationDictionary;
+    }
+
+
+    private String parseOptionalArgument()
+    throws IOException
+    {
+        readNextWord();
+
+        // Didn't the user specify a file name?
+        if (configurationEnd())
+        {
+            return "";
+        }
+
+        String fileName = nextWord;
+
+        readNextWord();
+
+        return fileName;
+    }
+
+
+    private boolean parseNoArgument(boolean value)
+    throws IOException
+    {
+        readNextWord();
+
+        return value;
+    }
+
+
+    private List parseClassSpecificationArguments(List    classSpecifications,
+                                                  boolean markClassFiles,
+                                                  boolean markConditionally)
+    throws ParseException, IOException
+    {
+        // Create a new List if necessary.
+        if (classSpecifications == null)
+        {
+            classSpecifications = new ArrayList();
+        }
+
+        // Read and add the keep configuration.
+        classSpecifications.add(parseClassSpecificationArguments(markClassFiles,
+                                                                 markConditionally));
+
+        return classSpecifications;
+    }
+
+
+    private ClassSpecification parseClassSpecificationArguments(boolean markClassFiles,
+                                                                boolean markConditionally)
+    throws ParseException, IOException
+    {
+        // Remember the comments preceeding this option.
+        String comments = lastComments;
+
+        // Parse the class access modifiers, if any.
+        int requiredSetClassAccessFlags   = 0;
+        int requiredUnsetClassAccessFlags = 0;
+
+        while (true)
+        {
+            readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD + "'" +
+                         " or '" + ClassConstants.EXTERNAL_ACC_INTERFACE + "'");
+
+            if (ConfigurationConstants.CLASS_KEYWORD.equals(nextWord))
+            {
+                // The class keyword. Stop parsing the class access modifiers.
+                break;
+            }
+
+            // Strip the negating sign, if any.
+            String strippedWord = nextWord.startsWith(ConfigurationConstants.NEGATOR_KEYWORD) ?
+                nextWord.substring(1) :
+                nextWord;
+
+            int accessFlag =
+                strippedWord.equals(ClassConstants.EXTERNAL_ACC_PUBLIC)    ? ClassConstants.INTERNAL_ACC_PUBLIC    :
+                strippedWord.equals(ClassConstants.EXTERNAL_ACC_FINAL)     ? ClassConstants.INTERNAL_ACC_FINAL     :
+                strippedWord.equals(ClassConstants.EXTERNAL_ACC_INTERFACE) ? ClassConstants.INTERNAL_ACC_INTERFACE :
+                strippedWord.equals(ClassConstants.EXTERNAL_ACC_ABSTRACT)  ? ClassConstants.INTERNAL_ACC_ABSTRACT  :
+                                                                             unknownAccessFlag();
+            if (strippedWord == nextWord)
+            {
+                requiredSetClassAccessFlags   |= accessFlag;
+            }
+            else
+            {
+                requiredUnsetClassAccessFlags |= accessFlag;
+            }
+
+
+            if ((requiredSetClassAccessFlags &
+                 requiredUnsetClassAccessFlags) != 0)
+            {
+                throw new ParseException("Conflicting class access modifiers for '" + strippedWord +
+                                         "' before " + reader.locationDescription());
+            }
+
+            if (ClassConstants.EXTERNAL_ACC_INTERFACE.equals(strippedWord))
+            {
+                // The interface keyword. Stop parsing the class flags.
+                break;
+            }
+        }
+
+        readNextWord("class name or interface name");
+        checkJavaIdentifier("class name or interface name");
+
+        // Parse the class name part. For backward compatibility, allow a
+        // single "*" wildcard to match any class.
+        String externalClassName = nextWord;
+        String className = ConfigurationConstants.ANY_CLASS_KEYWORD.equals(externalClassName) ?
+            null :
+            ClassUtil.internalClassName(externalClassName);
+
+        readNextWord();
+
+        String extendsClassName = null;
+
+        if (!configurationEnd())
+        {
+            // Parse 'implements ...' or 'extends ...' part, if any.
+            if (ConfigurationConstants.IMPLEMENTS_KEYWORD.equals(nextWord) ||
+                ConfigurationConstants.EXTENDS_KEYWORD.equals(nextWord))
+            {
+                readNextWord("class name or interface name");
+                checkJavaIdentifier("class name or interface name");
+                extendsClassName = ClassUtil.internalClassName(nextWord);
+
+                readNextWord();
+            }
+        }
+
+        // Create the basic class specification.
+        ClassSpecification classSpecification =
+            new ClassSpecification(requiredSetClassAccessFlags,
+                                   requiredUnsetClassAccessFlags,
+                                   className,
+                                   extendsClassName,
+                                   markClassFiles,
+                                   markConditionally,
+                                   comments);
+
+
+        // Now modify this ClassSpecification, adding any class members.
+        if (!configurationEnd())
+        {
+            // Check the class member opening part.
+            if (!ConfigurationConstants.OPEN_KEYWORD.equals(nextWord))
+            {
+                throw new ParseException("Expecting opening '" + ConfigurationConstants.OPEN_KEYWORD +
+                                         "' at " + reader.locationDescription());
+            }
+
+            // Parse all class members.
+            while (parseClassMemberSpecificationArguments(externalClassName,
+                                                          classSpecification));
+        }
+
+        return classSpecification;
+    }
+
+
+    private boolean parseClassMemberSpecificationArguments(String             externalClassName,
+                                                           ClassSpecification classSpecification) throws ParseException, IOException
+    {
+        // Parse the class member access modifiers, if any.
+        int requiredSetMemberAccessFlags   = 0;
+        int requiredUnsetMemberAccessFlags = 0;
+
+        while (true)
+        {
+            readNextWord("class member description" +
+                         " or closing '" + ConfigurationConstants.CLOSE_KEYWORD + "'");
+
+            if (requiredSetMemberAccessFlags   == 0 &&
+                requiredUnsetMemberAccessFlags == 0 &&
+                ConfigurationConstants.CLOSE_KEYWORD.equals(nextWord))
+            {
+                // The closing brace. Stop parsing the class members.
+                readNextWord();
+
+                return false;
+            }
+
+            String strippedWord = nextWord.startsWith("!") ?
+                nextWord.substring(1) :
+                nextWord;
+
+            int accessFlag =
+                strippedWord.equals(ClassConstants.EXTERNAL_ACC_PUBLIC)       ? ClassConstants.INTERNAL_ACC_PUBLIC       :
+                strippedWord.equals(ClassConstants.EXTERNAL_ACC_PRIVATE)      ? ClassConstants.INTERNAL_ACC_PRIVATE      :
+                strippedWord.equals(ClassConstants.EXTERNAL_ACC_PROTECTED)    ? ClassConstants.INTERNAL_ACC_PROTECTED    :
+                strippedWord.equals(ClassConstants.EXTERNAL_ACC_STATIC)       ? ClassConstants.INTERNAL_ACC_STATIC       :
+                strippedWord.equals(ClassConstants.EXTERNAL_ACC_FINAL)        ? ClassConstants.INTERNAL_ACC_FINAL        :
+                strippedWord.equals(ClassConstants.EXTERNAL_ACC_SYNCHRONIZED) ? ClassConstants.INTERNAL_ACC_SYNCHRONIZED :
+                strippedWord.equals(ClassConstants.EXTERNAL_ACC_VOLATILE)     ? ClassConstants.INTERNAL_ACC_VOLATILE     :
+                strippedWord.equals(ClassConstants.EXTERNAL_ACC_TRANSIENT)    ? ClassConstants.INTERNAL_ACC_TRANSIENT    :
+                strippedWord.equals(ClassConstants.EXTERNAL_ACC_NATIVE)       ? ClassConstants.INTERNAL_ACC_NATIVE       :
+                strippedWord.equals(ClassConstants.EXTERNAL_ACC_ABSTRACT)     ? ClassConstants.INTERNAL_ACC_ABSTRACT     :
+                strippedWord.equals(ClassConstants.EXTERNAL_ACC_STRICT)       ? ClassConstants.INTERNAL_ACC_STRICT       :
+                                                                                0;
+            if (accessFlag == 0)
+            {
+                // Not a class member access modifier. Stop parsing them.
+                break;
+            }
+
+            if (strippedWord == nextWord)
+            {
+                requiredSetMemberAccessFlags   |= accessFlag;
+            }
+            else
+            {
+                requiredUnsetMemberAccessFlags |= accessFlag;
+            }
+
+            // Make sure the user doesn't try to set and unset the same
+            // access flags simultaneously.
+            if ((requiredSetMemberAccessFlags &
+                 requiredUnsetMemberAccessFlags) != 0)
+            {
+                throw new ParseException("Conflicting class member access modifiers for " +
+                                         reader.locationDescription());
+            }
+        }
+
+        // Parse the class member type and name part.
+
+        // Did we get a special wildcard?
+        if (ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD.equals(nextWord) ||
+            ConfigurationConstants.ANY_FIELD_KEYWORD       .equals(nextWord) ||
+            ConfigurationConstants.ANY_METHOD_KEYWORD      .equals(nextWord))
+        {
+            // Act according to the type of wildcard..
+            if (ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD.equals(nextWord))
+            {
+                checkFieldAccessFlags(requiredSetMemberAccessFlags,
+                                      requiredUnsetMemberAccessFlags);
+                checkMethodAccessFlags(requiredSetMemberAccessFlags,
+                                       requiredUnsetMemberAccessFlags);
+
+                classSpecification.addField(
+                    new ClassMemberSpecification(requiredSetMemberAccessFlags,
+                                                 requiredUnsetMemberAccessFlags,
+                                                 null,
+                                                 null));
+                classSpecification.addMethod(
+                    new ClassMemberSpecification(requiredSetMemberAccessFlags,
+                                                 requiredUnsetMemberAccessFlags,
+                                                 null,
+                                                 null));
+            }
+            else if (ConfigurationConstants.ANY_FIELD_KEYWORD.equals(nextWord))
+            {
+                checkFieldAccessFlags(requiredSetMemberAccessFlags,
+                                      requiredUnsetMemberAccessFlags);
+
+                classSpecification.addField(
+                    new ClassMemberSpecification(requiredSetMemberAccessFlags,
+                                                 requiredUnsetMemberAccessFlags,
+                                                 null,
+                                                 null));
+            }
+            else if (ConfigurationConstants.ANY_METHOD_KEYWORD.equals(nextWord))
+            {
+                checkMethodAccessFlags(requiredSetMemberAccessFlags,
+                                       requiredUnsetMemberAccessFlags);
+
+                classSpecification.addMethod(
+                    new ClassMemberSpecification(requiredSetMemberAccessFlags,
+                                                 requiredUnsetMemberAccessFlags,
+                                                 null,
+                                                 null));
+            }
+
+            // We still have to read the closing separator.
+            readNextWord("separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + "'");
+
+            if (!ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord))
+            {
+                throw new ParseException("Expecting separator '" + ConfigurationConstants.SEPARATOR_KEYWORD +
+                                         "' before " + reader.locationDescription());
+            }
+        }
+        else
+        {
+            // Make sure we have a proper type.
+            checkJavaIdentifier("class member type");
+            String type = nextWord;
+
+            readNextWord("class member name");
+            String name = nextWord;
+
+            // Did we get just one word before the opening parenthesis?
+            if (ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(name))
+            {
+                // This must be a constructor then.
+                // Make sure the type is a proper constructor name.
+                if (!(type.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ||
+                      type.equals(externalClassName) ||
+                      type.equals(ClassUtil.externalShortClassName(externalClassName))))
+                {
+                    throw new ParseException("Expecting type and name " +
+                                             "instead of just '" + type +
+                                             "' before " + reader.locationDescription());
+                }
+
+                // Assign the fixed constructor type and name.
+                type = ClassConstants.EXTERNAL_TYPE_VOID;
+                name = ClassConstants.INTERNAL_METHOD_NAME_INIT;
+            }
+            else
+            {
+                // It's not a constructor.
+                // Make sure we have a proper name.
+                checkJavaIdentifier("class member name");
+
+                // Read the opening parenthesis or the separating
+                // semi-colon.
+                readNextWord("opening '" + ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD +
+                             "' or separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + "'");
+            }
+
+            // Are we looking at a field, a method, or something else?
+            if (ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord))
+            {
+                // It's a field.
+                checkFieldAccessFlags(requiredSetMemberAccessFlags,
+                                      requiredUnsetMemberAccessFlags);
+
+                // We already have a field descriptor.
+                String descriptor = ClassUtil.internalType(type);
+
+                // Add the field.
+                classSpecification.addField(
+                    new ClassMemberSpecification(requiredSetMemberAccessFlags,
+                                                 requiredUnsetMemberAccessFlags,
+                                                 name,
+                                                 descriptor));
+            }
+            else if (ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(nextWord))
+            {
+                // It's a method.
+                checkMethodAccessFlags(requiredSetMemberAccessFlags,
+                                       requiredUnsetMemberAccessFlags);
+
+                // Parse the method arguments.
+                String descriptor =
+                    ClassUtil.internalMethodDescriptor(type,
+                                                       parseCommaSeparatedList(true, false));
+
+                if (!ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD.equals(nextWord))
+                {
+                    throw new ParseException("Expecting separating '" + ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD +
+                                             "' or closing '" + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD +
+                                             "' before " + reader.locationDescription());
+                }
+
+                // Read the separator after the closing parenthesis.
+                readNextWord("separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + "'");
+
+                if (!ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord))
+                {
+                    throw new ParseException("Expecting separator '" + ConfigurationConstants.SEPARATOR_KEYWORD +
+                                             "' before " + reader.locationDescription());
+                }
+
+                // Add the method.
+                classSpecification.addMethod(
+                    new ClassMemberSpecification(requiredSetMemberAccessFlags,
+                                                 requiredUnsetMemberAccessFlags,
+                                                 name,
+                                                 descriptor));
+            }
+            else
+            {
+                // It doesn't look like a field or a method.
+                throw new ParseException("Expecting opening '" + ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD +
+                                         "' or separator '" + ConfigurationConstants.SEPARATOR_KEYWORD +
+                                         "' before " + reader.locationDescription());
+            }
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Reads a comma-separated list of java identifiers or of file names,
+     * optionally ending after the closing parenthesis.
+     */
+    private List parseCommaSeparatedList(boolean checkJavaIdentifiers,
+                                         boolean replaceSystemProperties)
+    throws ParseException, IOException
+    {
+        List arguments = new ArrayList();
+
+        while (true)
+        {
+            // Read an argument.
+            readNextWord("argument");
+
+            if (arguments.size() == 0 &&
+                (ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD.equals(nextWord) ||
+                 ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord)))
+            {
+                break;
+            }
+
+            if (checkJavaIdentifiers)
+            {
+                checkJavaIdentifier("argument type");
+            }
+
+            if (replaceSystemProperties)
+            {
+                nextWord = replaceSystemProperties(nextWord);
+            }
+
+            arguments.add(nextWord);
+
+            // Read a comma (or a closing parenthesis, or a different word).
+            readNextWord("separating '" + ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD +
+                         "' or closing '" + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD +
+                         "'");
+
+            if (!ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD.equals(nextWord))
+            {
+                break;
+            }
+        }
+
+        return arguments;
+    }
+
+
+    /**
+     * Throws a ParseException for an unexpected keyword.
+     */
+    private int unknownAccessFlag() throws ParseException
+    {
+        throw new ParseException("Unexpected keyword " + reader.locationDescription());
+    }
+
+
+    /**
+     * Replaces any system properties in the given word by their values
+     * (e.g. the substring "<java.home>" is replaced by its value).
+     */
+    private String replaceSystemProperties(String word)
+    throws ParseException
+    {
+        int fromIndex = 0;
+        while (true)
+        {
+            fromIndex = word.indexOf(ConfigurationConstants.OPEN_SYSTEM_PROPERTY, fromIndex);
+            if (fromIndex < 0)
+            {
+                break;
+            }
+
+            int toIndex = word.indexOf(ConfigurationConstants.CLOSE_SYSTEM_PROPERTY, fromIndex+1);
+            if (toIndex < 0)
+            {
+                throw new ParseException("Expecting closing '" + ConfigurationConstants.CLOSE_SYSTEM_PROPERTY +
+                                         "' after opening '" + ConfigurationConstants.OPEN_SYSTEM_PROPERTY +
+                                         "' in " + reader.locationDescription());
+            }
+
+            String propertyName  = word.substring(fromIndex+1, toIndex);
+            String propertyValue = System.getProperty(propertyName);
+            if (propertyValue == null)
+            {
+                throw new ParseException("Value of system property '" + propertyName +
+                                         "' is undefined in " + reader.locationDescription());
+            }
+
+            word = word.substring(0, fromIndex) +
+                       propertyValue +
+                       word.substring(toIndex+1);
+        }
+
+        return word;
+    }
+
+
+    /**
+     * Reads the next word of the configuration in the 'nextWord' field,
+     * throwing an exception if there is no next word.
+     */
+    private void readNextWord(String expectedDescription)
+    throws ParseException, IOException
+    {
+        readNextWord();
+        if (configurationEnd())
+        {
+            throw new ParseException("Expecting " + expectedDescription +
+                                     " before " + reader.locationDescription());
+        }
+    }
+
+
+    /**
+     * Reads the next word of the configuration in the 'nextWord' field.
+     */
+    private void readNextWord()
+    throws IOException
+    {
+        nextWord = reader.nextWord();
+    }
+
+
+    /**
+     * Returns whether the end of the configuration has been reached.
+     */
+    private boolean configurationEnd()
+    {
+        return nextWord == null ||
+               nextWord.startsWith(ConfigurationConstants.OPTION_PREFIX) ||
+               nextWord.equals(ConfigurationConstants.AT_DIRECTIVE);
+    }
+
+
+    /**
+     * Checks whether the given word is a valid Java identifier and throws
+     * a ParseException if it isn't. Wildcard characters are accepted.
+     */
+    private void checkJavaIdentifier(String expectedDescription)
+    throws ParseException
+    {
+        if (!isJavaIdentifier(nextWord))
+        {
+            throw new ParseException("Expecting " + expectedDescription +
+                                     " before " + reader.locationDescription());
+        }
+    }
+
+
+    /**
+     * Returns whether the given word is a valid Java identifier.
+     * Wildcard characters are accepted.
+     */
+    private boolean isJavaIdentifier(String aWord)
+    {
+        for (int index = 0; index < aWord.length(); index++)
+        {
+            char c = aWord.charAt(index);
+            if (!(Character.isJavaIdentifierPart(c) ||
+                  c == '.' ||
+                  c == '[' ||
+                  c == ']' ||
+                  c == '<' ||
+                  c == '>' ||
+                  c == '*' ||
+                  c == '?' ||
+                  c == '%'))
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Checks whether the given access flags are valid field access flags,
+     * throwing a ParseException if they aren't.
+     */
+    private void checkFieldAccessFlags(int requiredSetMemberAccessFlags,
+                                       int requiredUnsetMemberAccessFlags)
+    throws ParseException
+    {
+        if (((requiredSetMemberAccessFlags |
+              requiredUnsetMemberAccessFlags) &
+            ~ClassConstants.VALID_INTERNAL_ACC_FIELD) != 0)
+        {
+            throw new ParseException("Invalid method access modifier for field before " +
+                                     reader.locationDescription());
+        }
+    }
+
+
+    /**
+     * Checks whether the given access flags are valid method access flags,
+     * throwing a ParseException if they aren't.
+     */
+    private void checkMethodAccessFlags(int requiredSetMemberAccessFlags,
+                                        int requiredUnsetMemberAccessFlags)
+    throws ParseException
+    {
+        if (((requiredSetMemberAccessFlags |
+              requiredUnsetMemberAccessFlags) &
+            ~ClassConstants.VALID_INTERNAL_ACC_METHOD) != 0)
+        {
+            throw new ParseException("Invalid field access modifier for method before " +
+                                     reader.locationDescription());
+        }
+    }
+
+
+    /**
+     * A main method for testing configuration parsing.
+     */
+    public static void main(String[] args) {
+        try
+        {
+            ConfigurationParser parser = new ConfigurationParser(args);
+
+            parser.parse(new Configuration());
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/src/proguard/ConfigurationWriter.java b/src/proguard/ConfigurationWriter.java
new file mode 100644
index 0000000..2c3a447
--- /dev/null
+++ b/src/proguard/ConfigurationWriter.java
@@ -0,0 +1,418 @@
+/* $Id: ConfigurationWriter.java,v 1.13 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+import proguard.classfile.*;
+import proguard.classfile.util.*;
+import proguard.util.*;
+
+import java.io.*;
+import java.util.*;
+
+
+/**
+ * This class writes ProGuard configurations to a file.
+ *
+ * @author Eric Lafortune
+ */
+public class ConfigurationWriter
+{
+    private static final String[] KEEP_NAMES_OPTIONS = new String[]
+    {
+        ConfigurationConstants.KEEP_NAMES_OPTION,
+        ConfigurationConstants.KEEP_CLASS_MEMBER_NAMES_OPTION,
+        ConfigurationConstants.KEEP_CLASSES_WITH_MEMBER_NAMES_OPTION
+    };
+
+    private static final String[] KEEP_OPTIONS = new String[]
+    {
+        ConfigurationConstants.KEEP_OPTION,
+        ConfigurationConstants.KEEP_CLASS_MEMBERS_OPTION,
+        ConfigurationConstants.KEEP_CLASSES_WITH_MEMBERS_OPTION
+    };
+
+    private static final String[] ASSUME_NO_SIDE_EFFECT_OPTIONS = new String[]
+    {
+        ConfigurationConstants.ASSUME_NO_SIDE_EFFECTS_OPTION,
+        ConfigurationConstants.ASSUME_NO_SIDE_EFFECTS_OPTION,
+        ConfigurationConstants.ASSUME_NO_SIDE_EFFECTS_OPTION
+    };
+
+
+    private PrintWriter writer;
+
+
+    /**
+     * Creates a new ConfigurationWriter for the given file name.
+     */
+    public ConfigurationWriter(String configurationFile) throws IOException
+    {
+        this(new PrintWriter(new FileWriter(configurationFile)));
+    }
+
+
+    /**
+     * Creates a new ConfigurationWriter for the given OutputStream.
+     */
+    public ConfigurationWriter(OutputStream outputStream) throws IOException
+    {
+        this(new PrintWriter(outputStream));
+    }
+
+
+    /**
+     * Creates a new ConfigurationWriter for the given PrintWriter.
+     */
+    public ConfigurationWriter(PrintWriter writer) throws IOException
+    {
+        this.writer = writer;
+    }
+
+
+    /**
+     * Closes this ConfigurationWriter.
+     */
+    public void close() throws IOException
+    {
+        writer.close();
+    }
+
+
+    /**
+     * Writes the given configuration.
+     * @param configuration the configuration that is to be written out.
+     * @throws IOException if an IO error occurs while writing the configuration.
+     */
+    public void write(Configuration configuration) throws IOException
+    {
+        // Write the program class path (input and output entries).
+        writeJarOptions(ConfigurationConstants.INJARS_OPTION,
+                        ConfigurationConstants.OUTJARS_OPTION,
+                        configuration.programJars);
+        writer.println();
+
+        // Write the library class path (output entries only).
+        writeJarOptions(ConfigurationConstants.LIBRARYJARS_OPTION,
+                        ConfigurationConstants.LIBRARYJARS_OPTION,
+                        configuration.libraryJars);
+        writer.println();
+
+        // Write the other options.
+        writeOption(ConfigurationConstants.DONT_SKIP_NON_PUBLIC_LIBRARY_CLASSES_OPTION,       !configuration.skipNonPublicLibraryClasses);
+        writeOption(ConfigurationConstants.DONT_SKIP_NON_PUBLIC_LIBRARY_CLASS_MEMBERS_OPTION, !configuration.skipNonPublicLibraryClassMembers);
+
+        writeOption(ConfigurationConstants.DONT_SHRINK_OPTION,                                !configuration.shrink);
+        writeOption(ConfigurationConstants.PRINT_USAGE_OPTION,                                configuration.printUsage);
+
+        writeOption(ConfigurationConstants.DONT_OPTIMIZE_OPTION ,                             !configuration.optimize);
+        writeOption(ConfigurationConstants.ALLOW_ACCESS_MODIFICATION_OPTION,                  configuration.allowAccessModification);
+
+        writeOption(ConfigurationConstants.DONT_OBFUSCATE_OPTION,                             !configuration.obfuscate);
+        writeOption(ConfigurationConstants.PRINT_MAPPING_OPTION,                              configuration.printMapping);
+        writeOption(ConfigurationConstants.APPLY_MAPPING_OPTION,                              configuration.applyMapping);
+        writeOption(ConfigurationConstants.OBFUSCATION_DICTIONARY_OPTION,                     configuration.obfuscationDictionary);
+        writeOption(ConfigurationConstants.OVERLOAD_AGGRESSIVELY_OPTION,                      configuration.overloadAggressively);
+        writeOption(ConfigurationConstants.DEFAULT_PACKAGE_OPTION,                            configuration.defaultPackage);
+        writeOption(ConfigurationConstants.DONT_USE_MIXED_CASE_CLASS_NAMES_OPTION,            !configuration.useMixedCaseClassNames);
+        writeOption(ConfigurationConstants.KEEP_ATTRIBUTES_OPTION,                            ListUtil.commaSeparatedString(configuration.keepAttributes));
+        writeOption(ConfigurationConstants.RENAME_SOURCE_FILE_ATTRIBUTE_OPTION,               configuration.newSourceFileAttribute);
+
+        writeOption(ConfigurationConstants.VERBOSE_OPTION,                                    configuration.verbose);
+        writeOption(ConfigurationConstants.DONT_NOTE_OPTION,                                  !configuration.note);
+        writeOption(ConfigurationConstants.DONT_WARN_OPTION,                                  !configuration.warn);
+        writeOption(ConfigurationConstants.IGNORE_WARNINGS_OPTION,                            configuration.ignoreWarnings);
+
+        writeOption(ConfigurationConstants.PRINT_SEEDS_OPTION,                                configuration.printSeeds);
+        writer.println();
+
+        // Write the keep options.
+        writeOptions(KEEP_OPTIONS, configuration.keep);
+
+        // Write the keep names options.
+        writeOptions(KEEP_NAMES_OPTIONS, configuration.keepNames);
+
+        // Write the "no side effect methods" options.
+        writeOptions(ASSUME_NO_SIDE_EFFECT_OPTIONS, configuration.assumeNoSideEffects);
+}
+
+
+    private void writeJarOptions(String    inputEntryOptionName,
+                                 String    outputEntryOptionName,
+                                 ClassPath classPath)
+    {
+        if (classPath != null)
+        {
+            for (int index = 0; index < classPath.size(); index++)
+            {
+                ClassPathEntry entry = classPath.get(index);
+                String optionName = entry.isOutput() ?
+                     outputEntryOptionName :
+                     inputEntryOptionName;
+
+                writer.print(optionName + " " + quotedString(entry.getName()));
+
+                // Append the filters, if any.
+                boolean filtered = false;
+
+                filtered = writeFilter(filtered, entry.getZipFilter());
+                filtered = writeFilter(filtered, entry.getEarFilter());
+                filtered = writeFilter(filtered, entry.getWarFilter());
+                filtered = writeFilter(filtered, entry.getJarFilter());
+                filtered = writeFilter(filtered, entry.getFilter());
+
+                if (filtered)
+                {
+                    writer.print(ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD);
+                }
+
+                writer.println();
+            }
+        }
+    }
+
+
+    private boolean writeFilter(boolean filtered, String filter)
+    {
+        if (filtered)
+        {
+            writer.print(ConfigurationConstants.SEPARATOR_KEYWORD);
+        }
+
+        if (filter != null)
+        {
+            if (!filtered)
+            {
+                writer.print(ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD);
+            }
+
+            writer.print(quotedString(filter));
+
+            filtered = true;
+        }
+
+        return filtered;
+    }
+
+
+    private void writeOption(String optionName, boolean flag)
+    {
+        if (flag)
+        {
+            writer.println(optionName);
+        }
+    }
+
+
+    private void writeOption(String optionName, String arguments)
+    {
+        if (arguments != null)
+        {
+            writer.println(optionName + " " + quotedString(arguments));
+        }
+    }
+
+
+    private void writeOptions(String[] optionNames,
+                              List     classSpecifications)
+    {
+        if (classSpecifications != null)
+        {
+            for (int index = 0; index < classSpecifications.size(); index++)
+            {
+                writeOption(optionNames,
+                                              (ClassSpecification)classSpecifications.get(index));
+            }
+        }
+    }
+
+
+    private void writeOption(String[]           optionNames,
+                             ClassSpecification classSpecification)
+    {
+        writer.println();
+
+        // Write out the comments for this option.
+        writeComments(classSpecification.comments);
+
+        // Write out the proper class specification option name.
+        writer.print(optionNames[classSpecification.markConditionally ? 2 :
+                                 classSpecification.markClassFiles    ? 0 :
+                                                                        1]);
+
+        writer.print(" ");
+
+        // Write out the class access flags.
+        writer.print(ClassUtil.externalClassAccessFlags(classSpecification.requiredUnsetAccessFlags,
+                                                        ConfigurationConstants.NEGATOR_KEYWORD));
+
+        writer.print(ClassUtil.externalClassAccessFlags(classSpecification.requiredSetAccessFlags));
+
+        // Write out the class keyword, if we didn't write the interface
+        // keyword earlier.
+        if (((classSpecification.requiredSetAccessFlags |
+              classSpecification.requiredUnsetAccessFlags) &
+             ClassConstants.INTERNAL_ACC_INTERFACE) == 0)
+        {
+            writer.print("class");
+        }
+
+        writer.print(" ");
+
+        // Write out the class name.
+        writer.print(classSpecification.className != null ?
+            ClassUtil.externalClassName(classSpecification.className) :
+            "*");
+
+        // Write out the extends template, if any.
+        if (classSpecification.extendsClassName != null)
+        {
+            writer.print(" extends " + ClassUtil.externalClassName(classSpecification.extendsClassName));
+        }
+
+        // Write out the keep field and keep method options, if any.
+        if (classSpecification.fieldSpecifications  != null ||
+            classSpecification.methodSpecifications != null)
+        {
+            writer.println(" {");
+
+            writeFieldSpecification( classSpecification.fieldSpecifications);
+            writeMethodSpecification(classSpecification.methodSpecifications,
+                                   classSpecification.className);
+            writer.println("}");
+        }
+        else
+        {
+            writer.println();
+        }
+    }
+
+
+
+    private void writeComments(String comments)
+    {
+        if (comments != null)
+        {
+            int index = 0;
+            while (index < comments.length())
+            {
+                int breakIndex = comments.indexOf('\n', index);
+                if (breakIndex < 0)
+                {
+                    breakIndex = comments.length();
+                }
+
+                writer.print('#');
+                writer.println(comments.substring(index, breakIndex));
+
+                index = breakIndex + 1;
+            }
+        }
+    }
+
+
+    private void writeFieldSpecification(List classMemberSpecifications)
+    {
+        if (classMemberSpecifications != null)
+        {
+            for (int index = 0; index < classMemberSpecifications.size(); index++)
+            {
+                ClassMemberSpecification classMemberSpecification =
+                    (ClassMemberSpecification)classMemberSpecifications.get(index);
+
+                writer.print("    ");
+
+                // Write out the field access flags.
+                writer.print(ClassUtil.externalFieldAccessFlags(classMemberSpecification.requiredUnsetAccessFlags,
+                                                                ConfigurationConstants.NEGATOR_KEYWORD));
+
+                writer.print(ClassUtil.externalFieldAccessFlags(classMemberSpecification.requiredSetAccessFlags));
+
+                // Write out the field name and descriptor.
+                writer.print(classMemberSpecification.name       != null ||
+                             classMemberSpecification.descriptor != null ?
+                    ClassUtil.externalFullFieldDescription(0,
+                                                           classMemberSpecification.name,
+                                                           classMemberSpecification.descriptor) :
+                    ConfigurationConstants.ANY_FIELD_KEYWORD);
+
+                writer.println(";");
+            }
+        }
+    }
+
+
+    private void writeMethodSpecification(List classMemberSpecifications, String className)
+    {
+        if (classMemberSpecifications != null)
+        {
+            for (int index = 0; index < classMemberSpecifications.size(); index++)
+            {
+                ClassMemberSpecification classMemberSpecification =
+                    (ClassMemberSpecification)classMemberSpecifications.get(index);
+
+                writer.print("    ");
+
+                // Write out the method access flags.
+                writer.print(ClassUtil.externalMethodAccessFlags(classMemberSpecification.requiredUnsetAccessFlags,
+                                                                 ConfigurationConstants.NEGATOR_KEYWORD));
+
+                writer.print(ClassUtil.externalMethodAccessFlags(classMemberSpecification.requiredSetAccessFlags));
+
+                // Write out the method name and descriptor.
+                writer.print(classMemberSpecification.name       != null ||
+                             classMemberSpecification.descriptor != null ?
+                    ClassUtil.externalFullMethodDescription(className,
+                                                            0,
+                                                            classMemberSpecification.name,
+                                                            classMemberSpecification.descriptor) :
+                    ConfigurationConstants.ANY_METHOD_KEYWORD);
+
+                writer.println(";");
+            }
+        }
+    }
+
+
+    private String quotedString(String string)
+    {
+        return
+            string.length()     == 0 ||
+            string.indexOf(' ') >= 0  ? ("'" + string + "'") :
+                                        (      string      );
+    }
+
+
+    /**
+     * A main method for testing configuration writing.
+     */
+    public static void main(String[] args) {
+        try
+        {
+            ConfigurationWriter writer = new ConfigurationWriter(args[0]);
+
+            writer.write(new Configuration());
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/src/proguard/DataEntryReaderFactory.java b/src/proguard/DataEntryReaderFactory.java
new file mode 100644
index 0000000..e4258d4
--- /dev/null
+++ b/src/proguard/DataEntryReaderFactory.java
@@ -0,0 +1,138 @@
+/* $Id: DataEntryReaderFactory.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+import proguard.io.*;
+import proguard.util.*;
+
+
+/**
+ * This class can create DataEntryReader instances based on class path entries.
+ * The readers will unwrap the input data entries from any jars, wars, ears,
+ * and zips, before passing them to a given reader.
+ *
+ * @author Eric Lafortune
+ */
+public class DataEntryReaderFactory
+{
+    /**
+     * Creates a DataEntryReader that can read the given class path entry.
+     *
+     * @param messagePrefix  a prefix for messages that are printed out.
+     * @param classPathEntry the input class path entry.
+     * @param reader         a data entry reader to which the reading of actual
+     *                       class files and resource files can be delegated.
+     * @return a DataEntryReader for reading the given class path entry.
+     */
+    public static DataEntryReader createDataEntryReader(String          messagePrefix,
+                                                        ClassPathEntry  classPathEntry,
+                                                        DataEntryReader reader)
+    {
+        String entryName = classPathEntry.getName();
+        boolean isJar = entryName.endsWith(".jar");
+        boolean isWar = entryName.endsWith(".war");
+        boolean isEar = entryName.endsWith(".ear");
+        boolean isZip = entryName.endsWith(".zip");
+
+        String filter    = classPathEntry.getFilter();
+        String jarFilter = classPathEntry.getJarFilter();
+        String warFilter = classPathEntry.getWarFilter();
+        String earFilter = classPathEntry.getEarFilter();
+        String zipFilter = classPathEntry.getZipFilter();
+
+        System.out.println(messagePrefix +
+                           (isJar ? "jar" :
+                            isWar ? "war" :
+                            isEar ? "ear" :
+                            isZip ? "zip" :
+                                    "directory") +
+                           " [" + classPathEntry.getName() + "]" +
+                           (filter    != null ||
+                            jarFilter != null ||
+                            warFilter != null ||
+                            earFilter != null ||
+                            zipFilter != null ? " (filtered)" : ""));
+
+        // Add a filter, if specified.
+        if (filter != null)
+        {
+            reader = new FilteredDataEntryReader(new DataEntryNameFilter(new FileNameListMatcher(filter)),
+                                                 reader);
+        }
+
+        // Unzip any jars, if necessary.
+        reader = wrapInJarReader(reader, isJar, jarFilter, ".jar");
+        if (!isJar)
+        {
+            // Unzip any wars, if necessary.
+            reader = wrapInJarReader(reader, isWar, warFilter, ".war");
+            if (!isWar)
+            {
+                // Unzip any ears, if necessary.
+                reader = wrapInJarReader(reader, isEar, earFilter, ".ear");
+                if (!isEar)
+                {
+                    // Unzip any zips, if necessary.
+                    reader = wrapInJarReader(reader, isZip, zipFilter, ".zip");
+                }
+            }
+        }
+
+        return reader;
+    }
+
+
+    /**
+     *  Wraps the given DataEntryReader in a JarReader, filtering it if necessary.
+     */
+    private static DataEntryReader wrapInJarReader(DataEntryReader reader,
+                                                   boolean         isJar,
+                                                   String          jarFilter,
+                                                   String          jarExtension)
+    {
+        // Unzip any jars, if necessary.
+        DataEntryReader jarReader = new JarReader(reader);
+
+        if (isJar)
+        {
+            // Always unzip.
+            return jarReader;
+        }
+        else
+        {
+            // Add a filter, if specified.
+            if (jarFilter != null)
+            {
+                jarReader = new FilteredDataEntryReader(
+                            new DataEntryNameFilter(
+                            new FileNameListMatcher(jarFilter)),
+                                jarReader);
+            }
+
+            // Only unzip the right type of jars.
+            return new FilteredDataEntryReader(
+                   new DataEntryNameFilter(
+                   new ExtensionMatcher(jarExtension)),
+                       jarReader,
+                       reader);
+        }
+    }
+}
diff --git a/src/proguard/DataEntryWriterFactory.java b/src/proguard/DataEntryWriterFactory.java
new file mode 100644
index 0000000..318afe7
--- /dev/null
+++ b/src/proguard/DataEntryWriterFactory.java
@@ -0,0 +1,152 @@
+/* $Id: DataEntryWriterFactory.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+import proguard.io.*;
+import proguard.util.*;
+
+import java.io.*;
+
+
+/**
+ * This class can create DataEntryWriter instances based on class paths. The
+ * writers will wrap the output in the proper jars, wars, ears, and zips.
+ *
+ * @author Eric Lafortune
+ */
+public class DataEntryWriterFactory
+{
+    /**
+     * Creates a DataEntryWriter that can write to the given class path entries.
+     *
+     * @param classPath the output class path.
+     * @param fromIndex the start index in the class path.
+     * @param toIndex   the end index in the class path.
+     * @return a DataEntryWriter for writing to the given class path entries.
+     */
+    public static DataEntryWriter createDataEntryWriter(ClassPath classPath,
+                                                        int       fromIndex,
+                                                        int       toIndex)
+    {
+        DataEntryWriter writer = null;
+
+        // Create a chain of writers, one for each class path entry.
+        for (int index = toIndex - 1; index >= fromIndex; index--)
+        {
+            ClassPathEntry entry = classPath.get(index);
+            writer = createClassPathEntryWriter(entry, writer);
+        }
+
+        return writer;
+    }
+
+
+    /**
+     * Creates a DataEntryWriter that can write to the given class path entry,
+     * or delegate to another DataEntryWriter if its filters don't match.
+     */
+    private static DataEntryWriter createClassPathEntryWriter(ClassPathEntry  classPathEntry,
+                                                              DataEntryWriter alternativeWriter)
+    {
+        String entryName = classPathEntry.getName();
+        boolean isJar = entryName.endsWith(".jar");
+        boolean isWar = entryName.endsWith(".war");
+        boolean isEar = entryName.endsWith(".ear");
+        boolean isZip = entryName.endsWith(".zip");
+
+        String filter    = classPathEntry.getFilter();
+        String jarFilter = classPathEntry.getJarFilter();
+        String warFilter = classPathEntry.getWarFilter();
+        String earFilter = classPathEntry.getEarFilter();
+        String zipFilter = classPathEntry.getZipFilter();
+
+        System.out.println("Preparing output " +
+                           (isJar ? "jar" :
+                            isWar ? "war" :
+                            isWar ? "ear" :
+                            isZip ? "zip" :
+                                    "directory") +
+                           " [" + classPathEntry.getName() + "]" +
+                           (filter    != null ||
+                            jarFilter != null ||
+                            warFilter != null ||
+                            earFilter != null ||
+                            zipFilter != null ? " (filtered)" : ""));
+
+        DataEntryWriter writer = new DirectoryWriter(new File(entryName),
+                                                     isJar ||
+                                                     isWar ||
+                                                     isEar ||
+                                                     isZip);
+
+        // Set up the filtered jar writers.
+        writer = wrapInJarWriter(writer, isZip, zipFilter, ".zip", isJar || isWar || isEar);
+        writer = wrapInJarWriter(writer, isEar, earFilter, ".ear", isJar || isWar);
+        writer = wrapInJarWriter(writer, isWar, warFilter, ".war", isJar);
+        writer = wrapInJarWriter(writer, isJar, jarFilter, ".jar", false);
+
+        // Add a filter, if specified.
+        writer = filter != null?
+            new FilteredDataEntryWriter(
+            new DataEntryNameFilter(
+            new FileNameListMatcher(filter)),
+                writer) :
+            writer;
+
+        // Let the writer cascade, if specified.
+        return alternativeWriter != null ?
+            new CascadingDataEntryWriter(writer, alternativeWriter) :
+            writer;
+    }
+
+
+    /**
+     * Wraps the given DataEntryWriter in a JarWriter, filtering if necessary.
+     */
+    private static DataEntryWriter wrapInJarWriter(DataEntryWriter writer,
+                                                   boolean         isJar,
+                                                   String          jarFilter,
+                                                   String          jarExtension,
+                                                   boolean         dontWrap)
+    {
+        // Zip up jars, if necessary.
+        DataEntryWriter jarWriter = dontWrap ?
+            (DataEntryWriter)new ParentDataEntryWriter(writer) :
+            (DataEntryWriter)new JarWriter(writer, null, ProGuard.VERSION);
+
+        // Add a filter, if specified.
+        DataEntryWriter filteredJarWriter = jarFilter != null?
+            new FilteredDataEntryWriter(
+            new DataEntryParentFilter(
+            new DataEntryNameFilter(
+            new FileNameListMatcher(jarFilter))),
+                 jarWriter) :
+            jarWriter;
+
+        // Only zip up jars, unless the output is a jar file itself.
+        return new FilteredDataEntryWriter(
+               new DataEntryParentFilter(
+               new DataEntryNameFilter(
+               new ExtensionMatcher(jarExtension))),
+                   filteredJarWriter,
+                   isJar ? jarWriter : writer);
+    }
+}
diff --git a/src/proguard/FileWordReader.java b/src/proguard/FileWordReader.java
new file mode 100644
index 0000000..6e2a5c6
--- /dev/null
+++ b/src/proguard/FileWordReader.java
@@ -0,0 +1,87 @@
+/* $Id: FileWordReader.java,v 1.9 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+import java.io.*;
+import java.net.URL;
+
+
+/**
+ * A <code>WordReader</code> that returns words from a file or a URL.
+ *
+ * @author Eric Lafortune
+ */
+public class FileWordReader extends WordReader
+{
+    private String           fileName;
+    private LineNumberReader reader;
+
+
+    /**
+     * Creates a new FileWordReader for the given file name.
+     */
+    public FileWordReader(String fileName) throws IOException
+    {
+        this.fileName = fileName;
+        this.reader   = new LineNumberReader(
+                        new BufferedReader(
+                        new FileReader(fileName)));
+    }
+
+
+    /**
+     * Creates a new FileWordReader for the given URL.
+     */
+    public FileWordReader(URL url) throws IOException
+    {
+        this.fileName = url.getPath();
+        this.reader   = new LineNumberReader(
+                        new BufferedReader(
+                        new InputStreamReader(
+                        url.openStream())));
+    }
+
+
+    /**
+     * Closes the FileWordReader.
+     */
+    public void close() throws IOException
+    {
+        if (reader != null)
+        {
+            reader.close();
+        }
+    }
+
+
+    // Implementations for WordReader.
+
+    protected String nextLine() throws IOException
+    {
+        return reader.readLine();
+    }
+
+
+    protected String lineLocationDescription()
+    {
+        return "line " + reader.getLineNumber() + " of file '" + fileName + "'";
+    }
+}
diff --git a/src/proguard/ParseException.java b/src/proguard/ParseException.java
new file mode 100644
index 0000000..82e61a3
--- /dev/null
+++ b/src/proguard/ParseException.java
@@ -0,0 +1,51 @@
+/* $Id: ParseException.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+
+/**
+ * This <code>Exception</code> signals that a parse exception of some
+ * sort has occurred.
+ *
+ * @author Eric Lafortune
+ */
+public class ParseException extends Exception {
+
+    /**
+     * Constructs a <code>ParseException</code> with <code>null</code>
+     * as its error detail message.
+     */
+    public ParseException() {
+        super();
+    }
+
+    /**
+     * Constructs a <code>ParseException</code> with the specified detail
+     * message. The error message string <code>s</code> can later be
+     * retrieved by the <code>{@link java.lang.Throwable#getMessage}</code>
+     * method of class <code>java.lang.Throwable</code>.
+     *
+     * @param s the detail message.
+     */
+    public ParseException(String s) {
+        super(s);
+    }
+}
diff --git a/src/proguard/ProGuard.java b/src/proguard/ProGuard.java
new file mode 100644
index 0000000..33d76b3
--- /dev/null
+++ b/src/proguard/ProGuard.java
@@ -0,0 +1,936 @@
+/* $Id: ProGuard.java,v 1.84 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.editor.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+import proguard.io.*;
+import proguard.obfuscate.*;
+import proguard.optimize.*;
+import proguard.optimize.evaluation.*;
+import proguard.optimize.peephole.*;
+import proguard.shrink.*;
+
+import java.io.*;
+
+
+/**
+ * Tool for shrinking, optimizing, and obfuscating Java class files.
+ *
+ * @author Eric Lafortune
+ */
+public class ProGuard
+{
+    public static final String VERSION = "ProGuard, version 3.2";
+
+    private Configuration configuration;
+    private ClassPool     programClassPool = new ClassPool();
+    private ClassPool     libraryClassPool = new ClassPool();
+
+
+    /**
+     * Creates a new ProGuard object to process jars as specified by the given
+     * configuration.
+     */
+    public ProGuard(Configuration configuration)
+    {
+        this.configuration = configuration;
+    }
+
+
+    /**
+     * Performs all subsequent ProGuard operations.
+     */
+    public void execute() throws IOException
+    {
+        System.out.println(VERSION);
+
+        readInput();
+
+        if (configuration.shrink   ||
+            configuration.optimize ||
+            configuration.obfuscate)
+        {
+            initialize();
+        }
+
+        if (configuration.printSeeds != null)
+        {
+            printSeeds();
+        }
+
+        if (configuration.shrink)
+        {
+            shrink();
+        }
+
+        if (configuration.optimize)
+        {
+            optimize();
+
+            // Shrink again, if we may.
+            if (configuration.shrink)
+            {
+                configuration.printUsage = null;
+
+                shrink();
+            }
+        }
+
+        if (configuration.obfuscate)
+        {
+            obfuscate();
+        }
+
+        if (configuration.shrink   ||
+            configuration.optimize ||
+            configuration.obfuscate)
+        {
+            sortConstantPools();
+        }
+
+        if (configuration.programJars.hasOutput())
+        {
+            writeOutput();
+        }
+
+        if (configuration.dump != null)
+        {
+            dump();
+        }
+    }
+
+
+    /**
+     * Reads the input jars (or directories).
+     */
+    private void readInput() throws IOException
+    {
+        if (configuration.verbose)
+        {
+            System.out.println("Reading jars...");
+        }
+
+        // Check if we have at least some program jars.
+        if (configuration.programJars == null)
+        {
+            throw new IOException("The input is empty. You have to specify one or more '-injars' options.");
+        }
+
+        // Read the input program jars.
+        readInput("Reading program ",
+                  configuration.programJars,
+                  createDataEntryClassPoolFiller(false));
+
+        // Check if we have at least some input class files.
+        if (programClassPool.size() == 0)
+        {
+            throw new IOException("The input doesn't contain any class files. Did you specify the proper '-injars' options?");
+        }
+
+        // Read all library jars.
+        if (configuration.libraryJars != null)
+        {
+            readInput("Reading library ",
+                      configuration.libraryJars,
+                      createDataEntryClassPoolFiller(true));
+        }
+
+        // The defaultPackage option implies the allowAccessModification option.
+        if (configuration.defaultPackage != null)
+        {
+            configuration.allowAccessModification = true;
+        }
+    }
+
+
+    /**
+     * Creates a DataEntryReader that will decode class files and put them in
+     * the proper class pool.
+     */
+    private DataEntryReader createDataEntryClassPoolFiller(boolean isLibrary)
+    {
+        // Get the proper class pool.
+        ClassPool classPool = isLibrary ?
+            libraryClassPool :
+            programClassPool;
+
+        // Prepare a data entry reader to filter all class files,
+        // which are then decoded to class files by a class file reader,
+        // which are then put in the class pool by a class pool filler.
+        return
+            new ClassFileFilter(
+            new ClassFileReader(isLibrary,
+                                configuration.skipNonPublicLibraryClasses,
+                                configuration.skipNonPublicLibraryClassMembers,
+            new ClassPoolFiller(classPool, configuration.note)));
+    }
+
+
+    /**
+     * Reads all input entries from the given class path.
+     */
+    private void readInput(String          messagePrefix,
+                           ClassPath       classPath,
+                           DataEntryReader reader) throws IOException
+    {
+        readInput(messagePrefix,
+                  classPath,
+                  0,
+                  classPath.size(),
+                  reader);
+    }
+
+
+    /**
+     * Reads all input entries from the given section of the given class path.
+     */
+    private void readInput(String          messagePrefix,
+                           ClassPath       classPath,
+                           int             fromIndex,
+                           int             toIndex,
+                           DataEntryReader reader) throws IOException
+    {
+        for (int index = fromIndex; index < toIndex; index++)
+        {
+            ClassPathEntry entry = classPath.get(index);
+            if (!entry.isOutput())
+            {
+                readInput(messagePrefix, entry, reader);
+            }
+        }
+    }
+
+
+    /**
+     * Reads the given input class path entry.
+     */
+    private void readInput(String          messagePrefix,
+                           ClassPathEntry  classPathEntry,
+                           DataEntryReader dataEntryReader) throws IOException
+    {
+        try
+        {
+            // Create a reader that can unwrap jars, wars, ears, and zips.
+            DataEntryReader reader =
+                DataEntryReaderFactory.createDataEntryReader(messagePrefix,
+                                                             classPathEntry,
+                                                             dataEntryReader);
+
+            // Create the data entry pump.
+            DirectoryPump directoryPump =
+                new DirectoryPump(new File(classPathEntry.getName()));
+
+            // Pump the data entries into the reader.
+            directoryPump.pumpDataEntries(reader);
+        }
+        catch (IOException ex)
+        {
+            throw new IOException("Can't read [" + classPathEntry + "] (" + ex.getMessage() + ")");
+        }
+    }
+
+
+    /**
+     * Initializes the cross-references between all class files.
+     */
+    private void initialize() throws IOException
+    {
+        // Initialize the class hierarchy for program class files.
+        ClassFileHierarchyInitializer classFileHierarchyInitializer =
+            new ClassFileHierarchyInitializer(programClassPool,
+                                              libraryClassPool,
+                                              configuration.warn);
+
+        programClassPool.classFilesAccept(classFileHierarchyInitializer);
+
+        // Initialize the rest of the class hierarchy.
+        ClassFileHierarchyInitializer classFileHierarchyInitializer2 =
+            new ClassFileHierarchyInitializer(programClassPool,
+                                              libraryClassPool,
+                                              false);
+
+        libraryClassPool.classFilesAccept(classFileHierarchyInitializer2);
+
+        // Initialize the other class references.
+        ClassFileReferenceInitializer classFileReferenceInitializer =
+            new ClassFileReferenceInitializer(programClassPool,
+                                              libraryClassPool,
+                                              configuration.warn,
+                                              configuration.note);
+
+        programClassPool.classFilesAccept(classFileReferenceInitializer);
+
+        int noteCount = classFileReferenceInitializer.getNoteCount();
+        if (noteCount > 0)
+        {
+            System.err.println("Note: there were " + noteCount +
+                               " class casts of dynamically created class instances.");
+            System.err.println("      You might consider explicitly keeping the mentioned classes and/or");
+            System.err.println("      their implementations (using '-keep').");
+        }
+
+        int hierarchyWarningCount = classFileHierarchyInitializer.getWarningCount();
+        if (hierarchyWarningCount > 0)
+        {
+            System.err.println("Warning: there were " + hierarchyWarningCount +
+                               " unresolved references to superclasses or interfaces.");
+            System.err.println("         You may need to specify additional library jars (using '-libraryjars'),");
+            System.err.println("         or perhaps the '-dontskipnonpubliclibraryclasses' option.");
+        }
+
+        int referenceWarningCount = classFileReferenceInitializer.getWarningCount();
+        if (referenceWarningCount > 0)
+        {
+            System.err.println("Warning: there were " + referenceWarningCount +
+                               " unresolved references to program class members.");
+            System.err.println("         Your input class files appear to be inconsistent.");
+            System.err.println("         You may need to recompile them and try again.");
+        }
+
+        if ((hierarchyWarningCount > 0 ||
+             referenceWarningCount > 0) &&
+            !configuration.ignoreWarnings)
+        {
+            System.err.println("         If you are sure the mentioned classes are not used anyway,");
+            System.err.println("         you could try your luck using the '-ignorewarnings' option.");
+            throw new IOException("Please correct the above warnings first.");
+        }
+
+        // Discard unused library classes.
+        if (configuration.verbose)
+        {
+                    System.out.println("Removing unused library classes...");
+                    System.out.println("    Original number of library classes: " + libraryClassPool.size());
+        }
+
+        // Reinitialize the library class pool with only those library classes
+        // whose hierarchies are referenced by the program classes.
+        ClassPool newLibraryClassPool = new ClassPool();
+        programClassPool.classFilesAccept(
+            new AllCpInfoVisitor(
+            new ReferencedClassFileVisitor(
+            new LibraryClassFileFilter(
+            new ClassFileHierarchyTraveler(true, true, true, false,
+            new LibraryClassFileFilter(
+            new ClassPoolFiller(newLibraryClassPool, false)))))));
+
+        libraryClassPool = newLibraryClassPool;
+
+        if (configuration.verbose)
+        {
+            System.out.println("    Final number of library classes:    " + libraryClassPool.size());
+        }
+    }
+
+
+    /**
+     * Prints out classes and class members that are used as seeds in the
+     * shrinking and obfuscation steps.
+     */
+    private void printSeeds() throws IOException
+    {
+        if (configuration.verbose)
+        {
+            System.out.println("Printing kept classes, fields, and methods...");
+        }
+
+        // Check if we have at least some keep commands.
+        if (configuration.keep == null)
+        {
+            throw new IOException("You have to specify '-keep' options for the shrinking step.");
+        }
+
+        PrintStream ps = configuration.printSeeds.length() > 0 ?
+            new PrintStream(new BufferedOutputStream(new FileOutputStream(configuration.printSeeds))) :
+            System.out;
+
+        // Create a visitor for printing out the seeds. Note that we're only
+        // printing out the program elements that are preserved against shrinking.
+        SimpleClassFilePrinter printer = new SimpleClassFilePrinter(false, ps);
+        ClassPoolVisitor classPoolvisitor =
+            ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
+                                                                    new ProgramClassFileFilter(printer),
+                                                                    new ProgramMemberInfoFilter(printer));
+
+        // Print out the seeds.
+        programClassPool.accept(classPoolvisitor);
+        libraryClassPool.accept(classPoolvisitor);
+
+        if (ps != System.out)
+        {
+            ps.close();
+        }
+    }
+
+
+    /**
+     * Performs the shrinking step.
+     */
+    private void shrink() throws IOException
+    {
+        if (configuration.verbose)
+        {
+            System.out.println("Shrinking...");
+        }
+
+        // Check if we have at least some keep commands.
+        if (configuration.keep == null)
+        {
+            throw new IOException("You have to specify '-keep' options for the shrinking step.");
+        }
+
+        // Clean up any old visitor info.
+        ClassFileCleaner classFileCleaner = new ClassFileCleaner();
+        programClassPool.classFilesAccept(classFileCleaner);
+        libraryClassPool.classFilesAccept(classFileCleaner);
+
+        // Create a visitor for marking the seeds.
+        UsageMarker usageMarker = new UsageMarker();
+        ClassPoolVisitor classPoolvisitor =
+            ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
+                                                                    usageMarker,
+                                                                    usageMarker);
+        // Mark the seeds.
+        programClassPool.accept(classPoolvisitor);
+        libraryClassPool.accept(classPoolvisitor);
+
+        // Mark interfaces that have to be kept.
+        programClassPool.classFilesAccept(new InterfaceUsageMarker());
+
+        // Mark the inner class information that has to be kept.
+        programClassPool.classFilesAccept(new InnerUsageMarker());
+
+        if (configuration.printUsage != null)
+        {
+            if (configuration.verbose)
+            {
+                System.out.println("Printing usage" +
+                                   (configuration.printUsage.length() > 0 ?
+                                       " to [" + configuration.printUsage + "]" :
+                                       "..."));
+            }
+
+            PrintStream ps = configuration.printUsage.length() > 0 ?
+                new PrintStream(new BufferedOutputStream(new FileOutputStream(configuration.printUsage))) :
+                System.out;
+
+            // Print out items that will be removed.
+            programClassPool.classFilesAcceptAlphabetically(new UsagePrinter(true, ps));
+
+            if (ps != System.out)
+            {
+                ps.close();
+            }
+        }
+
+        // Discard unused program classes.
+        if (configuration.verbose)
+        {
+            System.out.println("Removing unused program classes and class elements...");
+            System.out.println("    Original number of program classes: " + programClassPool.size());
+        }
+
+        ClassPool newProgramClassPool = new ClassPool();
+        programClassPool.classFilesAccept(
+            new UsedClassFileFilter(
+            new MultiClassFileVisitor(
+            new ClassFileVisitor[] {
+                new ClassFileShrinker(1024),
+                new ClassPoolFiller(newProgramClassPool, false)
+            })));
+        programClassPool = newProgramClassPool;
+
+        if (configuration.verbose)
+        {
+            System.out.println("    Final number of program classes:    " + programClassPool.size());
+        }
+
+        // Check if we have at least some output class files.
+        if (programClassPool.size() == 0)
+        {
+            throw new IOException("The output jar is empty. Did you specify the proper '-keep' options?");
+        }
+    }
+
+
+    /**
+     * Performs the optimization step.
+     */
+    private void optimize() throws IOException
+    {
+        if (configuration.verbose)
+        {
+            System.out.println("Optimizing...");
+        }
+
+        // Clean up any old visitor info.
+        ClassFileCleaner classFileCleaner = new ClassFileCleaner();
+        programClassPool.classFilesAccept(classFileCleaner);
+        libraryClassPool.classFilesAccept(classFileCleaner);
+
+        // Check if we have at least some keep commands.
+        if (configuration.keep == null)
+        {
+            throw new IOException("You have to specify '-keep' options for the optimization step.");
+        }
+
+        // Create a visitor for marking the seeds.
+        KeepMarker keepMarker = new KeepMarker();
+        ClassPoolVisitor classPoolvisitor =
+            ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
+                                                                    keepMarker,
+                                                                    keepMarker);
+
+        // Mark the seeds.
+        programClassPool.accept(classPoolvisitor);
+        libraryClassPool.accept(classPoolvisitor);
+
+        // Make class files and methods final, as far as possible.
+        programClassPool.classFilesAccept(new ClassFileFinalizer());
+
+        // Mark all fields that are write-only.
+        programClassPool.classFilesAccept(
+            new AllMethodVisitor(
+            new AllAttrInfoVisitor(
+            new AllInstructionVisitor(
+            new WriteOnlyFieldMarker()))));
+
+        if (configuration.assumeNoSideEffects != null)
+        {
+            // Create a visitor for marking methods that don't have any side effects.
+            NoSideEffectMethodMarker noSideEffectMethodMarker = new NoSideEffectMethodMarker();
+            ClassPoolVisitor noClassPoolvisitor =
+                ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.assumeNoSideEffects,
+                                                                        null,
+                                                                        noSideEffectMethodMarker);
+
+            // Mark the seeds.
+            programClassPool.accept(noClassPoolvisitor);
+            libraryClassPool.accept(noClassPoolvisitor);
+        }
+
+        // Mark all methods that have side effects.
+        programClassPool.accept(new SideEffectMethodMarker());
+
+        // Mark all interfaces that have single implementations.
+        programClassPool.classFilesAccept(new SingleImplementationMarker(configuration.allowAccessModification));
+
+        // Inline interfaces with single implementations.
+        // First update the references to classes and class members.
+        // Then update the class member descriptors.
+        programClassPool.classFilesAccept(new AllMethodVisitor(
+                                          new AllAttrInfoVisitor(
+                                          new SingleImplementationInliner())));
+        programClassPool.classFilesAccept(new AllMemberInfoVisitor(
+                                          new SingleImplementationInliner()));
+
+        // Perform partial evaluation.
+        programClassPool.classFilesAccept(new AllMethodVisitor(
+                                          new PartialEvaluator()));
+
+        // Create a branch target marker and a code attribute editor that can
+        // be reused for all code attributes.
+        BranchTargetFinder branchTargetFinder = new BranchTargetFinder(1024);
+        CodeAttrInfoEditor codeAttrInfoEditor = new CodeAttrInfoEditor(1024);
+
+        // Visit all code attributes.
+        // First let the branch marker mark all branch targets.
+        // Then perform peephole optimisations on the instructions:
+        // - Remove push/pop instruction pairs.
+        // - Remove load/store instruction pairs.
+        // - Replace store/load instruction pairs by dup/store instructions.
+        // - Replace branches to return instructions by return instructions.
+        // - Remove nop instructions.
+        // - Inline simple getters and setters.
+        // Finally apply all changes to the code.
+        programClassPool.classFilesAccept(
+            new AllMethodVisitor(
+            new AllAttrInfoVisitor(
+            new MultiAttrInfoVisitor(
+            new AttrInfoVisitor[]
+            {
+                branchTargetFinder,
+                new CodeAttrInfoEditorResetter(codeAttrInfoEditor),
+                new AllInstructionVisitor(
+                new MultiInstructionVisitor(
+                new InstructionVisitor[]
+                {
+                    new PushPopRemover(branchTargetFinder, codeAttrInfoEditor),
+                    new LoadStoreRemover(branchTargetFinder, codeAttrInfoEditor),
+                    new StoreLoadReplacer(branchTargetFinder, codeAttrInfoEditor),
+                    new GotoReturnReplacer(codeAttrInfoEditor),
+                    new NopRemover(codeAttrInfoEditor),
+                    new GetterSetterInliner(codeAttrInfoEditor, configuration.allowAccessModification),
+                })),
+                codeAttrInfoEditor
+            }))));
+    }
+
+
+    /**
+     * Performs the obfuscation step.
+     */
+    private void obfuscate() throws IOException
+    {
+        if (configuration.verbose)
+        {
+            System.out.println("Obfuscating...");
+        }
+
+        // Check if we have at least some keep commands.
+        if (configuration.keep      == null &&
+            configuration.keepNames == null)
+        {
+            throw new IOException("You have to specify '-keep' options for the obfuscation step.");
+        }
+
+        // Clean up any old visitor info.
+        ClassFileCleaner classFileCleaner = new ClassFileCleaner();
+        programClassPool.classFilesAccept(classFileCleaner);
+        libraryClassPool.classFilesAccept(classFileCleaner);
+
+        // Link all class members that should get the same names.
+        programClassPool.classFilesAccept(new BottomClassFileFilter(
+                                          new MemberInfoLinker()));
+
+        // Create a visitor for marking the seeds.
+        NameMarker nameMarker = new NameMarker();
+        ClassPoolVisitor classPoolvisitor =
+            new MultiClassPoolVisitor(new ClassPoolVisitor[]
+            {
+                ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
+                                                                        nameMarker,
+                                                                        nameMarker),
+                ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keepNames,
+                                                                        nameMarker,
+                                                                        nameMarker)
+            });
+
+        // Mark the seeds.
+        programClassPool.accept(classPoolvisitor);
+        libraryClassPool.accept(classPoolvisitor);
+
+        // Apply the mapping, if one has been specified.
+        if (configuration.applyMapping != null)
+        {
+            if (configuration.verbose)
+            {
+                System.out.println("Applying mapping [" + configuration.applyMapping + "]");
+            }
+
+            MappingReader    reader = new MappingReader(configuration.applyMapping);
+            MappingProcessor keeper =
+                new MultiMappingProcessor(new MappingProcessor[]
+                {
+                    new MappingKeeper(programClassPool),
+                    new MappingKeeper(libraryClassPool),
+                });
+
+            reader.pump(keeper);
+        }
+
+        // Mark attributes that have to be kept.
+        AttributeUsageMarker attributeUsageMarker = new AttributeUsageMarker();
+        if (configuration.keepAttributes != null)
+        {
+            if (configuration.keepAttributes.size() != 0)
+            {
+                attributeUsageMarker.setKeepAttributes(configuration.keepAttributes);
+            }
+            else
+            {
+                attributeUsageMarker.setKeepAllAttributes();
+            }
+        }
+        programClassPool.classFilesAccept(attributeUsageMarker);
+
+        // Remove the attributes that can be discarded.
+        programClassPool.classFilesAccept(new AttributeShrinker());
+
+        if (configuration.verbose)
+        {
+            System.out.println("Renaming program classes and class elements...");
+        }
+
+        // Come up with new names for all class files.
+        programClassPool.classFilesAccept(new ClassFileObfuscator(programClassPool,
+                                                                  configuration.defaultPackage,
+                                                                  configuration.useMixedCaseClassNames));
+
+        // Come up with new names for all class members.
+        programClassPool.classFilesAccept(new BottomClassFileFilter(
+                                          new MemberInfoObfuscator(configuration.overloadAggressively,
+                                                                   configuration.obfuscationDictionary)));
+
+        // Print out the mapping, if requested.
+        if (configuration.printMapping != null)
+        {
+            if (configuration.verbose)
+            {
+                System.out.println("Printing mapping" +
+                                   (configuration.printMapping.length() > 0 ?
+                                       " to [" + configuration.printMapping + "]" :
+                                       "..."));
+            }
+
+            PrintStream ps = configuration.printMapping.length() > 0 ?
+                new PrintStream(new BufferedOutputStream(new FileOutputStream(configuration.printMapping))) :
+                System.out;
+
+            // Print out items that will be removed.
+            programClassPool.classFilesAcceptAlphabetically(new MappingPrinter(ps));
+
+            if (ps != System.out)
+            {
+                ps.close();
+            }
+        }
+
+        // Actually apply these new names.
+        programClassPool.classFilesAccept(new ClassFileRenamer(configuration.defaultPackage != null,
+                                                               configuration.newSourceFileAttribute));
+
+        // Mark NameAndType constant pool entries that have to be kept
+        // and remove the other ones.
+        programClassPool.classFilesAccept(new NameAndTypeUsageMarker());
+        programClassPool.classFilesAccept(new NameAndTypeShrinker(1024));
+
+        // Mark Utf8 constant pool entries that have to be kept
+        // and remove the other ones.
+        programClassPool.classFilesAccept(new Utf8UsageMarker());
+        programClassPool.classFilesAccept(new Utf8Shrinker(1024));
+    }
+
+
+    /**
+     * Sorts the constant pools of all program class files.
+     */
+    private void sortConstantPools()
+    {
+        programClassPool.classFilesAccept(new ConstantPoolSorter(1024));
+    }
+
+
+    /**
+     * Writes the output jars.
+     */
+    private void writeOutput() throws IOException
+    {
+        if (configuration.verbose)
+        {
+            System.out.println("Writing jars...");
+        }
+
+        ClassPath programJars = configuration.programJars;
+
+        // Perform a check on the first jar.
+        ClassPathEntry firstEntry = programJars.get(0);
+        if (firstEntry.isOutput())
+        {
+            throw new IOException("The output jar [" + firstEntry.getName() +
+                                  "] must be specified after an input jar, or it will be empty.");
+        }
+
+        // Perform some checks on the output jars.
+        for (int index = 0; index < programJars.size() - 1; index++)
+        {
+            ClassPathEntry entry = programJars.get(index);
+            if (entry.isOutput())
+            {
+                // Check if all but the last output jars have filters.
+                if (entry.getFilter()    == null &&
+                    entry.getJarFilter() == null &&
+                    entry.getWarFilter() == null &&
+                    entry.getEarFilter() == null &&
+                    entry.getZipFilter() == null &&
+                    programJars.get(index + 1).isOutput())
+                {
+                    throw new IOException("The output jar [" + entry.getName() +
+                                          "] must have a filter, or all subsequent jars will be empty.");
+                }
+
+                // Check if the output jar name is different from the input jar names.
+                for (int inIndex = 0; inIndex < programJars.size(); inIndex++)
+                {
+                    ClassPathEntry otherEntry = programJars.get(inIndex);
+
+                    if (!otherEntry.isOutput() &&
+                        entry.getName().equals(otherEntry.getName()))
+                    {
+                        throw new IOException("The output jar [" + entry.getName() +
+                                              "] must be different from all input jars.");
+                    }
+                }
+            }
+        }
+
+        int firstInputIndex = 0;
+        int lastInputIndex  = 0;
+
+        // Go over all program class path entries.
+        for (int index = 0; index < programJars.size(); index++)
+        {
+            // Is it an input entry?
+            ClassPathEntry entry = programJars.get(index);
+            if (!entry.isOutput())
+            {
+                // Remember the index of the last input entry.
+                lastInputIndex = index;
+            }
+            else
+            {
+                // Check if this the last output entry in a series.
+                int nextIndex = index + 1;
+                if (nextIndex == programJars.size() ||
+                    !programJars.get(nextIndex).isOutput())
+                {
+                    // Write the processed input entries to the output entries.
+                    writeOutput(programJars,
+                                firstInputIndex,
+                                lastInputIndex + 1,
+                                nextIndex);
+
+                    // Start with the next series of input entries.
+                    firstInputIndex = nextIndex;
+                }
+            }
+        }
+    }
+
+
+
+
+    /**
+     * Transfers the specified input jars to the specified output jars.
+     */
+    private void writeOutput(ClassPath classPath,
+                             int       fromInputIndex,
+                             int       fromOutputIndex,
+                             int       toOutputIndex)
+    throws IOException
+    {
+        try
+        {
+            // Construct the writer that can write jars, wars, ears, zips, and
+            // directories, cascading over the specified output entries.
+            DataEntryWriter writer =
+                DataEntryWriterFactory.createDataEntryWriter(classPath,
+                                                             fromOutputIndex,
+                                                             toOutputIndex);
+
+            // Create the reader that can write class files and copy resource
+            // files to the above writer.
+            DataEntryReader reader =
+                new ClassFileFilter(new ClassFileRewriter(programClassPool,
+                                                          writer),
+                                    new DataEntryCopier(writer));
+
+            // Read and handle the specified input entries.
+            readInput("Copying resources from program ",
+                      classPath,
+                      fromInputIndex,
+                      fromOutputIndex,
+                      reader);
+
+            // Close all output entries.
+            writer.close();
+        }
+        catch (IOException ex)
+        {
+            throw new IOException("Can't write [" + classPath.get(fromOutputIndex).getName() + "] (" + ex.getMessage() + ")");
+        }
+    }
+
+
+    /**
+     * Prints out the contents of the program class files.
+     */
+    private void dump() throws IOException
+    {
+        if (configuration.verbose)
+        {
+            System.out.println("Printing classes" +
+                               (configuration.dump.length() > 0 ?
+                                   " to [" + configuration.dump + "]" :
+                                   "..."));
+        }
+
+        PrintStream ps = configuration.dump.length() > 0 ?
+            new PrintStream(new BufferedOutputStream(new FileOutputStream(configuration.dump))) :
+            System.out;
+
+        programClassPool.classFilesAccept(new ClassFilePrinter(ps));
+
+        if (configuration.dump.length() > 0)
+        {
+            ps.close();
+        }
+    }
+
+
+    /**
+     * The main method for ProGuard.
+     */
+    public static void main(String[] args)
+    {
+        if (args.length == 0)
+        {
+            System.out.println("Usage: java proguard.ProGuard [options ...]");
+            System.exit(1);
+        }
+
+        // Create the default options.
+        Configuration configuration = new Configuration();
+
+        try
+        {
+            // Parse the options specified in the command line arguments.
+            ConfigurationParser parser = new ConfigurationParser(args);
+            parser.parse(configuration);
+
+            // Execute ProGuard with these options.
+            ProGuard proGuard = new ProGuard(configuration);
+            proGuard.execute();
+        }
+        catch (Exception ex)
+        {
+            if (configuration.verbose)
+            {
+                // Print a verbose stack trace.
+                ex.printStackTrace();
+            }
+            else
+            {
+                // Print just the stack trace message.
+                System.err.println("Error: "+ex.getMessage());
+            }
+
+            System.exit(1);
+        }
+
+        System.exit(0);
+    }
+}
diff --git a/src/proguard/SubclassedClassFileFilter.java b/src/proguard/SubclassedClassFileFilter.java
new file mode 100644
index 0000000..128bd84
--- /dev/null
+++ b/src/proguard/SubclassedClassFileFilter.java
@@ -0,0 +1,63 @@
+/* $Id: SubclassedClassFileFilter.java,v 1.10 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This ClassFileVisitor delegates all its method calls to another
+ * ClassFileVisitor, but only for ClassFile objects that are being subclassed.
+ *
+ * @author Eric Lafortune
+ */
+class SubclassedClassFileFilter
+implements ClassFileVisitor
+{
+    ClassFileVisitor classFileVisitor;
+
+
+    public SubclassedClassFileFilter(ClassFileVisitor classFileVisitor)
+    {
+        this.classFileVisitor = classFileVisitor;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        if (programClassFile.subClasses != null)
+        {
+            classFileVisitor.visitProgramClassFile(programClassFile);
+        }
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        if (libraryClassFile.subClasses != null)
+        {
+            classFileVisitor.visitLibraryClassFile(libraryClassFile);
+        }
+    }
+}
diff --git a/src/proguard/WordReader.java b/src/proguard/WordReader.java
new file mode 100644
index 0000000..4827335
--- /dev/null
+++ b/src/proguard/WordReader.java
@@ -0,0 +1,271 @@
+/* $Id: WordReader.java,v 1.15 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+import java.io.*;
+
+
+/**
+ * An abstract reader of words, with the possibility to include other readers.
+ * Words are separated by spaces or broken off at delimiters. Words containing
+ * spaces or delimiters can be quoted with single or double quotes.
+ * Comments (everything starting with '#' on a single line) are ignored.
+ *
+ * @author Eric Lafortune
+ */
+public abstract class WordReader
+{
+    private static final char COMMENT_CHARACTER = '#';
+
+
+    private WordReader includeWordReader;
+    private String     currentLine;
+    private int        currentLineLength;
+    private int        currentIndex;
+    private String     currentWord;
+    private String     currentComments;
+
+
+    /**
+     * Specifies to start reading words from the given WordReader. When it is
+     * exhausted, this WordReader will continue to provide its own words.
+     *
+     * @param newIncludeWordReader the WordReader that will start reading words.
+     */
+    public void includeWordReader(WordReader newIncludeWordReader)
+    {
+        if (includeWordReader == null)
+        {
+            includeWordReader = newIncludeWordReader;
+        }
+        else
+        {
+            includeWordReader.includeWordReader(newIncludeWordReader);
+        }
+    }
+
+
+    /**
+     * Reads a word from this WordReader, or from one of its active included
+     * WordReader objects.
+     *
+     * @return the read word.
+     */
+    public String nextWord() throws IOException
+    {
+        currentWord = null;
+
+        // See if we have an included reader to produce a word.
+        if (includeWordReader != null)
+        {
+            // Does the included word reader still produce a word?
+            currentWord = includeWordReader.nextWord();
+            if (currentWord != null)
+            {
+                // Return it if so.
+                return currentWord;
+            }
+
+            // Otherwise ditch the word reader.
+            includeWordReader = null;
+        }
+
+        // Get a word from this reader.
+
+        // Skip leading whitespace.
+        while (currentLine != null &&
+               currentIndex < currentLineLength &&
+               Character.isWhitespace(currentLine.charAt(currentIndex)))
+        {
+            currentIndex++;
+        }
+
+        // Make sure we have a non-blank line.
+        while (currentLine == null || currentIndex == currentLineLength)
+        {
+            currentLine = nextLine();
+            if (currentLine == null)
+            {
+                return null;
+            }
+
+            // Trim off any comments.
+            int comments_start = currentLine.indexOf(COMMENT_CHARACTER);
+            if (comments_start >= 0)
+            {
+                currentLineLength = comments_start;
+
+                // Remember the comments.
+                String comment = currentLine.substring(comments_start + 1);
+                currentComments = currentComments == null ?
+                    comment :
+                    currentComments + '\n' + comment;
+            }
+            else
+            {
+                currentLineLength = currentLine.length();
+            }
+
+            // Skip leading whitespace.
+            currentIndex = 0;
+            while (currentIndex < currentLineLength &&
+                   Character.isWhitespace(currentLine.charAt(currentIndex)))
+            {
+                currentIndex++;
+            }
+        }
+
+        // Find the word starting at the current index.
+        int startIndex = currentIndex;
+        int endIndex;
+
+        char startChar = currentLine.charAt(startIndex);
+
+        if (isDelimiter(startChar))
+        {
+            // The next word is a single delimiting character.
+            endIndex = ++currentIndex;
+        }
+        else if (isQuote(startChar))
+        {
+            // The next word is starting with a quote character.
+            // Skip the opening quote.
+            startIndex++;
+
+            // The next word is a quoted character string.
+            // Find the closing quote.
+            do
+            {
+                currentIndex++;
+
+                if (currentIndex == currentLineLength)
+                {
+                    currentWord = currentLine.substring(startIndex-1, currentIndex);
+                    throw new IOException("Missing closing quote for "+locationDescription());
+                }
+            }
+            while (currentLine.charAt(currentIndex) != startChar);
+
+            endIndex = currentIndex++;
+        }
+        else
+        {
+            // The next word is a simple character string.
+            // Find the end of the line, the first delimiter, or the first
+            // white space.
+            while (currentIndex < currentLineLength)
+            {
+                char currentCharacter = currentLine.charAt(currentIndex);
+                if (isDelimiter(currentCharacter) ||
+                    Character.isWhitespace(currentCharacter))
+                {
+                    break;
+                }
+
+                currentIndex++;
+            }
+
+            endIndex = currentIndex;
+        }
+
+        // Remember and return the parsed word.
+        currentWord = currentLine.substring(startIndex, endIndex);
+
+        return currentWord;
+    }
+
+
+    /**
+     * Returns the comments collected before returning the last word.
+     * Starts collecting new comments.
+     *
+     * @return the collected comments, or <code>null</code> if there weren't any.
+     */
+    public String lastComments() throws IOException
+    {
+        if (includeWordReader == null)
+        {
+            String comments = currentComments;
+            currentComments = null;
+            return comments;
+        }
+        else
+        {
+            return includeWordReader.lastComments();
+        }
+    }
+
+
+    /**
+     * Constructs a readable description of the current position in this
+     * WordReader and its included WordReader objects.
+     *
+     * @return the description.
+     */
+    public String locationDescription()
+    {
+        return
+            (includeWordReader == null ?
+                (currentWord == null ?
+                    "end of " :
+                    "'" + currentWord + "' in " ) :
+                (includeWordReader.locationDescription() + ",\n" +
+                 "  included from ")) +
+            lineLocationDescription();
+    }
+
+
+    /**
+     * Reads a line from this WordReader, or from one of its active included
+     * WordReader objects.
+     *
+     * @return the read line.
+     */
+    protected abstract String nextLine() throws IOException;
+
+
+    /**
+     * Constructs a readable description of the current WordReader position.
+     *
+     * @return the description.
+     */
+    protected abstract String lineLocationDescription();
+
+
+    private boolean isDelimiter(char character)
+    {
+        return character == '@' ||
+               character == '{' ||
+               character == '}' ||
+               character == '(' ||
+               character == ')' ||
+               character == ',' ||
+               character == ';' ||
+               character == File.pathSeparatorChar;
+    }
+
+
+    private boolean isQuote(char character)
+    {
+        return character == '\'' ||
+               character == '"';
+    }
+}
diff --git a/src/proguard/ant/ClassMemberSpecificationElement.java b/src/proguard/ant/ClassMemberSpecificationElement.java
new file mode 100644
index 0000000..062fe62
--- /dev/null
+++ b/src/proguard/ant/ClassMemberSpecificationElement.java
@@ -0,0 +1,201 @@
+/* $Id: ClassMemberSpecificationElement.java,v 1.4 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.ant;
+
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.types.*;
+import proguard.*;
+import proguard.classfile.*;
+import proguard.classfile.util.*;
+import proguard.util.*;
+
+import java.util.*;
+
+/**
+ * This DataType represents a class member specification in Ant.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassMemberSpecificationElement extends DataType
+{
+    private String access;
+    private String type;
+    private String name;
+    private String parameters;
+
+
+    /**
+     * Adds the contents of this class member specification element to the given
+     * list.
+     * @param classMemberSpecifications the class member specifications to be
+     *                                  extended.
+     * @param isMethod                  specifies whether this specification
+     *                                  refers to a method.
+     * @param isConstructor             specifies whether this specification
+     *                                  refers to a constructor.
+     */
+    public void appendTo(List    classMemberSpecifications,
+                         boolean isMethod,
+                         boolean isConstructor)
+    {
+        // Get the referenced file set, or else this one.
+        ClassMemberSpecificationElement classSpecificationElement = isReference() ?
+            (ClassMemberSpecificationElement)getCheckedRef(this.getClass(),
+                                                           this.getClass().getName()) :
+            this;
+
+        // Create a new class specification.
+        String access     = classSpecificationElement.access;
+        String type       = classSpecificationElement.type;
+        String name       = classSpecificationElement.name;
+        String parameters = classSpecificationElement.parameters;
+
+        // Perform some basic checks on the attributes.
+        if (isMethod)
+        {
+            if (isConstructor)
+            {
+                if (type != null)
+                {
+                    throw new BuildException("Type attribute not allowed in constructor specification ["+type+"]");
+                }
+
+                if (parameters != null)
+                {
+                    type = ClassConstants.EXTERNAL_TYPE_VOID;
+                }
+
+                name = ClassConstants.INTERNAL_METHOD_NAME_INIT;
+            }
+            else if ((type != null) ^ (parameters != null))
+            {
+                throw new BuildException("Type and parameters attributes must always be present in combination in method specification");
+            }
+        }
+        else
+        {
+            if (parameters != null)
+            {
+                throw new BuildException("Parameters attribute not allowed in field specification ["+parameters+"]");
+            }
+        }
+
+        List parameterList = ListUtil.commaSeparatedList(parameters);
+
+        String descriptor =
+            parameters != null ? ClassUtil.internalMethodDescriptor(type, parameterList) :
+            type       != null ? ClassUtil.internalType(type)                            :
+                                 null;
+
+        ClassMemberSpecification classMemberSpecification =
+            new ClassMemberSpecification(requiredAccessFlags(true,  access),
+                                         requiredAccessFlags(false, access),
+                                         name,
+                                         descriptor);
+
+        // Add it to the list.
+        classMemberSpecifications.add(classMemberSpecification);
+    }
+
+
+    // Ant task attributes.
+
+    public void setAccess(String access)
+    {
+        this.access = access;
+    }
+
+
+    public void setType(String type)
+    {
+        this.type = type;
+    }
+
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+
+    public void setParameters(String parameters)
+    {
+        this.parameters = parameters;
+    }
+
+
+    /**
+     * @deprecated Use {@link #setParameters(String)} instead.
+     */
+    public void setParam(String parameters)
+    {
+        this.parameters = parameters;
+    }
+
+
+    // Small utility methods.
+
+    private int requiredAccessFlags(boolean set,
+                                    String  access)
+    throws BuildException
+    {
+        int accessFlags = 0;
+
+        if (access != null)
+        {
+            StringTokenizer tokenizer = new StringTokenizer(access, " ,");
+            while (tokenizer.hasMoreTokens())
+            {
+                String token = tokenizer.nextToken();
+
+                if (token.startsWith("!") ^ set)
+                {
+                    String strippedToken = token.startsWith("!") ?
+                        token.substring(1) :
+                        token;
+
+                    int accessFlag =
+                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_PUBLIC)       ? ClassConstants.INTERNAL_ACC_PUBLIC       :
+                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_PRIVATE)      ? ClassConstants.INTERNAL_ACC_PRIVATE      :
+                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_PROTECTED)    ? ClassConstants.INTERNAL_ACC_PROTECTED    :
+                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_STATIC)       ? ClassConstants.INTERNAL_ACC_STATIC       :
+                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_FINAL)        ? ClassConstants.INTERNAL_ACC_FINAL        :
+                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_SYNCHRONIZED) ? ClassConstants.INTERNAL_ACC_SYNCHRONIZED :
+                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_VOLATILE)     ? ClassConstants.INTERNAL_ACC_VOLATILE     :
+                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_TRANSIENT)    ? ClassConstants.INTERNAL_ACC_TRANSIENT    :
+                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_NATIVE)       ? ClassConstants.INTERNAL_ACC_NATIVE       :
+                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_ABSTRACT)     ? ClassConstants.INTERNAL_ACC_ABSTRACT     :
+                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_STRICT)       ? ClassConstants.INTERNAL_ACC_STRICT       :
+                        0;
+
+                    if (accessFlag == 0)
+                    {
+                        throw new BuildException("Incorrect class member access modifier ["+strippedToken+"]");
+                    }
+
+                    accessFlags |= accessFlag;
+                }
+            }
+        }
+
+        return accessFlags;
+    }
+}
diff --git a/src/proguard/ant/ClassPathElement.java b/src/proguard/ant/ClassPathElement.java
new file mode 100644
index 0000000..e1d687a
--- /dev/null
+++ b/src/proguard/ant/ClassPathElement.java
@@ -0,0 +1,173 @@
+/* $Id: ClassPathElement.java,v 1.7 2004/12/18 20:21:11 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.ant;
+
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.types.*;
+
+import proguard.*;
+
+import java.io.*;
+import java.lang.ref.Reference;
+import java.util.Stack;
+
+/**
+ * This FileSet represents a class path entry (or a set of class path entries)
+ * in Ant.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassPathElement extends Path
+{
+    private String filter;
+    private String jarFilter;
+    private String warFilter;
+    private String earFilter;
+    private String zipFilter;
+
+
+    /**
+     * @see Path#Path(Project)
+     */
+    public ClassPathElement(Project project)
+    {
+        super(project);
+    }
+
+
+    /**
+     * Adds the contents of this class path element to the given class path.
+     * @param classPath the class path to be extended.
+     * @param output    specifies whether this is an output entry or not.
+     */
+    public void appendClassPathEntriesTo(ClassPath classPath, boolean output)
+    {
+        String   basePath = "";
+        String[] files;
+
+        if (isReference())
+        {
+            // Get the referenced path or file set.
+            Object referencedObject = getCheckedRef(DataType.class,
+                                                    DataType.class.getName());
+
+            if (referencedObject instanceof Path)
+            {
+                Path path = (Path)referencedObject;
+
+                // Get the names of the files in the referenced path.
+                files = path.list();
+            }
+            else if (referencedObject instanceof AbstractFileSet)
+            {
+                AbstractFileSet fileSet = (AbstractFileSet)referencedObject;
+
+                // Get the names of the existing input files in the referenced file set.
+                DirectoryScanner scanner = fileSet.getDirectoryScanner(getProject());
+                basePath = scanner.getBasedir().getPath() + File.separator;
+                files    = scanner.getIncludedFiles();
+            }
+            else
+            {
+                throw new BuildException("The refid attribute doesn't point to a <path> element or a <fileset> element");
+            }
+        }
+        else
+        {
+            // Get the names of the files in this path.
+            files = list();
+        }
+
+        for (int index = 0; index < files.length; index++)
+        {
+            // Create a new class path entry, with the proper file name and
+            // any filters.
+            ClassPathEntry entry = new ClassPathEntry(basePath + files[index], output);
+            entry.setFilter(filter);
+            entry.setJarFilter(jarFilter);
+            entry.setWarFilter(warFilter);
+            entry.setEarFilter(earFilter);
+            entry.setZipFilter(zipFilter);
+
+            // Add it to the class path.
+            classPath.add(entry);
+        }
+    }
+
+
+    // Ant task attributes.
+
+    /**
+     * @deprecated Use {@link #setLocation(File)} instead.
+     */
+    public void setFile(File file)
+    {
+        setLocation(file);
+    }
+
+
+    /**
+     * @deprecated Use {@link #setLocation(File)} instead.
+     */
+    public void setDir(File file)
+    {
+        setLocation(file);
+    }
+
+
+    /**
+     * @deprecated Use {@link #setLocation(File)} instead.
+     */
+    public void setName(File file)
+    {
+        setLocation(file);
+    }
+
+
+    public void setFilter(String filter)
+    {
+        this.filter = filter;
+    }
+
+
+    public void setJarFilter(String jarFilter)
+    {
+        this.jarFilter = jarFilter;
+    }
+
+
+    public void setWarFilter(String warFilter)
+    {
+        this.warFilter = warFilter;
+    }
+
+
+    public void setEarFilter(String earFilter)
+    {
+        this.earFilter = earFilter;
+    }
+
+
+    public void setZipFilter(String zipFilter)
+    {
+        this.zipFilter = zipFilter;
+    }
+}
diff --git a/src/proguard/ant/ClassSpecificationElement.java b/src/proguard/ant/ClassSpecificationElement.java
new file mode 100644
index 0000000..7da58af
--- /dev/null
+++ b/src/proguard/ant/ClassSpecificationElement.java
@@ -0,0 +1,230 @@
+/* $Id: ClassSpecificationElement.java,v 1.3 2004/12/18 20:21:43 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.ant;
+
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.types.*;
+import proguard.*;
+import proguard.classfile.*;
+import proguard.classfile.util.*;
+
+import java.util.*;
+
+/**
+ * This DataType represents a class specification in Ant.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassSpecificationElement extends DataType
+{
+    private static final String ANY_CLASS_KEYWORD  = "*";
+
+    private String access;
+    private String type;
+    private String name;
+    private String extends_;
+    private List   fieldSpecifications  = new ArrayList();
+    private List   methodSpecifications = new ArrayList();
+
+
+    /**
+     * Adds the contents of this class specification element to the given list.
+     * @param classSpecifications the class specifications to be extended.
+     * @param markClassFiles      specifies whether to mark the class files.
+     * @param markConditionally   specifies whether to mark the class files
+     *                            and class members conditionally.
+     */
+    public void appendTo(List    classSpecifications,
+                         boolean markClassFiles,
+                         boolean markConditionally)
+    {
+        // Get the referenced file set, or else this one.
+        ClassSpecificationElement classSpecificationElement = isReference() ?
+            (ClassSpecificationElement)getCheckedRef(this.getClass(),
+                                                     this.getClass().getName()) :
+            this;
+
+        // Create a new class specification.
+        String access   = classSpecificationElement.access;
+        String type     = classSpecificationElement.type;
+        String name     = classSpecificationElement.name;
+        String extends_ = classSpecificationElement.extends_;
+
+        // For backward compatibility, allow a single "*" wildcard to match
+        // any class.
+        if (name != null &&
+            name.equals(ANY_CLASS_KEYWORD))
+        {
+            name = null;
+        }
+
+        ClassSpecification classSpecification =
+            new ClassSpecification(requiredAccessFlags(true,  access, type),
+                                   requiredAccessFlags(false, access, type),
+                                   name     != null ? ClassUtil.internalClassName(name)     : null,
+                                   extends_ != null ? ClassUtil.internalClassName(extends_) : null,
+                                   markClassFiles,
+                                   markConditionally);
+
+        for (int index = 0; index < fieldSpecifications.size(); index++)
+        {
+            classSpecification.addField((ClassMemberSpecification)fieldSpecifications.get(index));
+        }
+
+        for (int index = 0; index < methodSpecifications.size(); index++)
+        {
+            classSpecification.addMethod((ClassMemberSpecification)methodSpecifications.get(index));
+        }
+
+        // Add it to the list.
+        classSpecifications.add(classSpecification);
+    }
+
+
+    // Ant task attributes.
+
+    public void setAccess(String access)
+    {
+        this.access = access;
+    }
+
+
+    public void setType(String type)
+    {
+        this.type = type;
+    }
+
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+
+    public void setExtends(String extends_)
+    {
+        this.extends_ = extends_;
+    }
+
+
+    public void setImplements(String implements_)
+    {
+        this.extends_ = implements_;
+    }
+
+
+    // Ant task nested elements.
+
+    public void addConfiguredField(ClassMemberSpecificationElement classMemberSpecificationElement)
+    {
+        if (fieldSpecifications == null)
+        {
+            fieldSpecifications = new ArrayList();
+        }
+
+        classMemberSpecificationElement.appendTo(fieldSpecifications,
+                                                 false,
+                                                 false);
+    }
+
+
+    public void addConfiguredMethod(ClassMemberSpecificationElement classMemberSpecificationElement)
+    {
+        if (methodSpecifications == null)
+        {
+            methodSpecifications = new ArrayList();
+        }
+
+        classMemberSpecificationElement.appendTo(methodSpecifications,
+                                                 true,
+                                                 false);
+    }
+
+
+    public void addConfiguredConstructor(ClassMemberSpecificationElement classMemberSpecificationElement)
+    {
+        if (methodSpecifications == null)
+        {
+            methodSpecifications = new ArrayList();
+        }
+
+        classMemberSpecificationElement.appendTo(methodSpecifications,
+                                                 true,
+                                                 true);
+    }
+
+
+    // Small utility methods.
+
+    private int requiredAccessFlags(boolean set,
+                                    String  access,
+                                    String  type)
+    throws BuildException
+    {
+        int accessFlags = 0;
+
+        if (access != null)
+        {
+            StringTokenizer tokenizer = new StringTokenizer(access, " ,");
+            while (tokenizer.hasMoreTokens())
+            {
+                String token = tokenizer.nextToken();
+
+                if (token.startsWith("!") ^ set)
+                {
+                    String strippedToken = token.startsWith("!") ?
+                        token.substring(1) :
+                        token;
+
+                    int accessFlag =
+                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_PUBLIC)   ? ClassConstants.INTERNAL_ACC_PUBLIC   :
+                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_FINAL)    ? ClassConstants.INTERNAL_ACC_FINAL    :
+                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_ABSTRACT) ? ClassConstants.INTERNAL_ACC_ABSTRACT :
+                        0;
+
+                    if (accessFlag == 0)
+                    {
+                        throw new BuildException("Incorrect class access modifier ["+strippedToken+"]");
+                    }
+
+                    accessFlags |= accessFlag;
+                }
+            }
+        }
+
+        if (type != null && (type.startsWith("!") ^ set))
+        {
+            int accessFlag =
+                type.equals(      ClassConstants.EXTERNAL_ACC_INTERFACE) ||
+                type.equals("!" + ClassConstants.EXTERNAL_ACC_INTERFACE) ? ClassConstants.INTERNAL_ACC_INTERFACE :
+                type.equals("class")                                     ? 0                                     :
+                                                                           -1;
+            if (accessFlag == -1)
+            {
+                throw new BuildException("Incorrect class type ["+type+"]");
+            }
+
+            accessFlags |= accessFlag;
+        }
+
+        return accessFlags;
+    }
+}
diff --git a/src/proguard/ant/ConfigurationElement.java b/src/proguard/ant/ConfigurationElement.java
new file mode 100644
index 0000000..e0ff9b1
--- /dev/null
+++ b/src/proguard/ant/ConfigurationElement.java
@@ -0,0 +1,59 @@
+/* $Id: ConfigurationElement.java,v 1.1 2004/08/28 16:37:12 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.ant;
+
+import proguard.*;
+
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.types.*;
+
+import java.util.*;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * This DataType represents a reference to a ProGuard configuration in Ant.
+ *
+ * @author Eric Lafortune
+ */
+public class ConfigurationElement extends DataType
+{
+    /**
+     * Adds the contents of this configuration task to the given configuration.
+     * @param configuration the configuration to be extended.
+     */
+    public void appendTo(Configuration configuration)
+    {
+        // Get the referenced element.
+        if (!isReference())
+        {
+            throw new BuildException("Nested element <configuration> must have a refid attribute");
+        }
+
+        ConfigurationTask configurationTask =
+            (ConfigurationTask)getCheckedRef(ConfigurationTask.class,
+                                             ConfigurationTask.class.getName());
+
+        // Append the referenced configuration entries to the given configuration.
+        configurationTask.appendTo(configuration);
+    }
+}
diff --git a/src/proguard/ant/ConfigurationTask.java b/src/proguard/ant/ConfigurationTask.java
new file mode 100644
index 0000000..8ddbc96
--- /dev/null
+++ b/src/proguard/ant/ConfigurationTask.java
@@ -0,0 +1,290 @@
+/* $Id: ConfigurationTask.java,v 1.3 2004/10/31 16:28:32 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.ant;
+
+import proguard.*;
+
+import org.apache.tools.ant.*;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * This Task allows to define a ProGuard configuration from Ant.
+ *
+ * @author Eric Lafortune
+ */
+public class ConfigurationTask extends Task
+{
+    protected Configuration configuration = new Configuration();
+
+
+    /**
+     * Adds the contents of this configuration task to the given configuration.
+     * @param configuration the configuration to be extended.
+     */
+    public void appendTo(Configuration configuration)
+    {
+        // Append all of these configuration entries to the given configuration.
+        configuration.programJars = extendClassPath(configuration.programJars,
+                                                    this.configuration.programJars);
+
+        configuration.libraryJars = extendClassPath(configuration.libraryJars,
+                                                    this.configuration.libraryJars);
+
+        configuration.keep = extendClassSpecifications(configuration.keep,
+                                                       this.configuration.keep);
+
+        configuration.keepNames = extendClassSpecifications(configuration.keepNames,
+                                                            this.configuration.keepNames);
+
+        configuration.keepAttributes = extendAttributes(configuration.keepAttributes,
+                                                        this.configuration.keepAttributes);
+    }
+
+
+    // Ant task nested elements.
+
+    public void addConfiguredInjar(ClassPathElement classPathElement)
+    {
+        configuration.programJars = extendClassPath(configuration.programJars,
+                                                    classPathElement,
+                                                    false);
+    }
+
+
+    public void addConfiguredOutjar(ClassPathElement classPathElement)
+    {
+        configuration.programJars = extendClassPath(configuration.programJars,
+                                                    classPathElement,
+                                                    true);
+    }
+
+
+    public void addConfiguredLibraryjar(ClassPathElement classPathElement)
+    {
+        configuration.libraryJars = extendClassPath(configuration.libraryJars,
+                                                    classPathElement,
+                                                    false);
+    }
+
+
+    public void addConfiguredKeep(ClassSpecificationElement classSpecificationElement)
+    {
+        configuration.keep = extendClassSpecifications(configuration.keep,
+                                                       classSpecificationElement,
+                                                       true,
+                                                       false);
+    }
+
+
+    public void addConfiguredKeepclassmembers(ClassSpecificationElement classSpecificationElement)
+    {
+        configuration.keep = extendClassSpecifications(configuration.keep,
+                                                       classSpecificationElement,
+                                                       false,
+                                                       false);
+    }
+
+
+    public void addConfiguredKeepclasseswithmembers(ClassSpecificationElement classSpecificationElement)
+    {
+        configuration.keep = extendClassSpecifications(configuration.keep,
+                                                       classSpecificationElement,
+                                                       true,
+                                                       true);
+    }
+
+
+    public void addConfiguredKeepnames(ClassSpecificationElement classSpecificationElement)
+    {
+        configuration.keepNames = extendClassSpecifications(configuration.keepNames,
+                                                            classSpecificationElement,
+                                                            true,
+                                                            false);
+    }
+
+
+    public void addConfiguredKeepclassmembernames(ClassSpecificationElement classSpecificationElement)
+    {
+        configuration.keepNames = extendClassSpecifications(configuration.keepNames,
+                                                            classSpecificationElement,
+                                                            false,
+                                                            false);
+    }
+
+
+    public void addConfiguredKeepclasseswithmembernames(ClassSpecificationElement classSpecificationElement)
+    {
+        configuration.keepNames = extendClassSpecifications(configuration.keepNames,
+                                                            classSpecificationElement,
+                                                            true,
+                                                            true);
+    }
+
+
+    public void addConfiguredKeepattribute(KeepAttributeElement keepAttributeElement)
+    {
+        configuration.keepAttributes = extendAttributes(configuration.keepAttributes,
+                                                        keepAttributeElement);
+    }
+
+
+    public void addConfiguredConfiguration(ConfigurationElement configurationElement)
+    {
+        configurationElement.appendTo(configuration);
+    }
+
+
+    // Implementations for Task.
+
+    public void addText(String text) throws BuildException
+    {
+        try
+        {
+            String arg = getProject().replaceProperties(text);
+            ConfigurationParser parser = new ConfigurationParser(new String[] { arg });
+            parser.parse(configuration);
+        }
+        catch (IOException ex)
+        {
+            throw new BuildException(ex.getMessage());
+        }
+        catch (ParseException ex)
+        {
+            throw new BuildException(ex.getMessage());
+        }
+    }
+
+
+    // Small utility methods.
+
+    private String optionalFileName(String fileName)
+    {
+        return
+            fileName.equalsIgnoreCase("false") ||
+            fileName.equalsIgnoreCase("no")    ||
+            fileName.equalsIgnoreCase("off")    ? null :
+            fileName.equalsIgnoreCase("true")  ||
+            fileName.equalsIgnoreCase("yes")   ||
+            fileName.equalsIgnoreCase("on")     ? ""   :
+                                                  fileName;
+    }
+
+
+    private ClassPath extendClassPath(ClassPath        classPath,
+                                      ClassPathElement classPathElement,
+                                      boolean          output)
+    {
+        if (classPath == null)
+        {
+            classPath = new ClassPath();
+        }
+
+        classPathElement.appendClassPathEntriesTo(classPath,
+                                                  output);
+
+        return classPath;
+    }
+
+
+    private ClassPath extendClassPath(ClassPath classPath,
+                                      ClassPath additionalClassPath)
+    {
+        if (additionalClassPath != null)
+        {
+            if (classPath == null)
+            {
+                classPath = new ClassPath();
+            }
+
+            classPath.addAll(additionalClassPath);
+        }
+
+        return classPath;
+    }
+
+
+    private List extendClassSpecifications(List                      classSpecifications,
+                                           ClassSpecificationElement classSpecificationElement,
+                                           boolean                   markClassFiles,
+                                           boolean                   markClassFilesConditionally)
+    {
+        if (classSpecifications == null)
+        {
+            classSpecifications = new ArrayList();
+        }
+
+        classSpecificationElement.appendTo(classSpecifications,
+                                           markClassFiles,
+                                           markClassFilesConditionally);
+
+        return classSpecifications;
+    }
+
+
+    private List extendClassSpecifications(List classSpecifications,
+                                           List additionalClassSpecifications)
+    {
+        if (additionalClassSpecifications != null)
+        {
+            if (classSpecifications == null)
+            {
+                classSpecifications = new ArrayList();
+            }
+
+            classSpecifications.addAll(additionalClassSpecifications);
+        }
+
+        return classSpecifications;
+    }
+
+
+    private List extendAttributes(List                 attributes,
+                                  KeepAttributeElement keepAttributeElement)
+    {
+        if (attributes == null)
+        {
+            attributes = new ArrayList();
+        }
+
+        keepAttributeElement.appendTo(attributes);
+
+        return attributes;
+    }
+
+
+    private List extendAttributes(List attributes,
+                                  List additionalAttributes)
+    {
+        if (additionalAttributes != null)
+        {
+            if (attributes == null)
+            {
+                attributes = new ArrayList();
+            }
+
+            attributes.addAll(additionalAttributes);
+        }
+
+        return attributes;
+    }
+}
diff --git a/src/proguard/ant/KeepAttributeElement.java b/src/proguard/ant/KeepAttributeElement.java
new file mode 100644
index 0000000..1ce0041
--- /dev/null
+++ b/src/proguard/ant/KeepAttributeElement.java
@@ -0,0 +1,70 @@
+/* $Id: KeepAttributeElement.java,v 1.2 2004/08/28 20:55:21 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.ant;
+
+import org.apache.tools.ant.types.*;
+
+import java.util.*;
+
+/**
+ * This DataType represents a named attribute in Ant.
+ *
+ * @author Eric Lafortune
+ */
+public class KeepAttributeElement extends DataType
+{
+    private String name;
+
+
+    /**
+     * Adds the contents of this element to the given list of attributes.
+     * @param keepAttributes the list of attributes to be extended.
+     */
+    public void appendTo(List keepAttributes)
+    {
+        // Get the referenced element, or else this one.
+        KeepAttributeElement keepAttributeElement = isReference() ?
+            (KeepAttributeElement)getCheckedRef(this.getClass(),
+                                                this.getClass().getName()) :
+            this;
+
+        String name = keepAttributeElement.name;
+
+        if (name == null)
+        {
+            // Clear the list to keep all attributes.
+            keepAttributes.clear();
+        }
+        else
+        {
+            // Add the attibute name to the list.
+            keepAttributes.add(name);
+        }
+    }
+
+
+    // Ant task attributes.
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+}
diff --git a/src/proguard/ant/ProGuardTask.java b/src/proguard/ant/ProGuardTask.java
new file mode 100644
index 0000000..a88fc2e
--- /dev/null
+++ b/src/proguard/ant/ProGuardTask.java
@@ -0,0 +1,216 @@
+/* $Id: ProGuardTask.java,v 1.28 2004/11/20 15:08:57 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.ant;
+
+import org.apache.tools.ant.*;
+import proguard.*;
+import proguard.classfile.util.*;
+
+import java.io.*;
+
+/**
+ * This Task allows to configure and run ProGuard from Ant.
+ *
+ * @author Eric Lafortune
+ */
+public class ProGuardTask extends ConfigurationTask
+{
+    // Ant task attributes.
+
+    public void setConfiguration(File configurationFile) throws BuildException
+    {
+        try
+        {
+            ConfigurationParser parser = new ConfigurationParser(configurationFile.getPath());
+            parser.parse(configuration);
+        }
+        catch (IOException ex)
+        {
+            throw new BuildException(ex.getMessage());
+        }
+        catch (ParseException ex)
+        {
+            throw new BuildException(ex.getMessage());
+        }
+    }
+
+
+    /**
+     * @deprecated Use the nested outjar element instead.
+     */
+    public void setOutjar(String parameters)
+    {
+        throw new BuildException("Use the <outjar> nested element instead of the 'outjar' attribute");
+    }
+
+
+    public void setSkipnonpubliclibraryclasses(boolean skipNonPublicLibraryClasses)
+    {
+        configuration.skipNonPublicLibraryClasses = skipNonPublicLibraryClasses;
+    }
+
+
+    public void setSkipnonpubliclibraryclassmembers(boolean skipNonPublicLibraryClassMembers)
+    {
+        configuration.skipNonPublicLibraryClassMembers = skipNonPublicLibraryClassMembers;
+    }
+
+
+    public void setPrintseeds(File printSeeds)
+    {
+        configuration.printSeeds = optionalFileName(printSeeds);
+    }
+
+
+    public void setShrink(boolean shrink)
+    {
+        configuration.shrink = shrink;
+    }
+
+
+    public void setPrintusage(File printUsage)
+    {
+        configuration.printUsage = optionalFileName(printUsage);
+    }
+
+
+    public void setOptimize(boolean optimize)
+    {
+        configuration.optimize = optimize;
+    }
+
+
+    public void setAllowaccessmodification(boolean allowAccessModification)
+    {
+        configuration.allowAccessModification = allowAccessModification;
+    }
+
+
+    public void setObfuscate(boolean obfuscate)
+    {
+        configuration.obfuscate = obfuscate;
+    }
+
+
+    public void setPrintmapping(File printMapping)
+    {
+        configuration.printMapping = optionalFileName(printMapping);
+    }
+
+
+    public void setApplymapping(String applyMapping)
+    {
+        configuration.applyMapping = applyMapping;
+    }
+
+
+    public void setObfuscationdictionary(File obfuscationDictionary)
+    {
+        configuration.obfuscationDictionary = obfuscationDictionary.getName();
+    }
+
+
+    public void setOverloadaggressively(boolean overloadAggressively)
+    {
+        configuration.overloadAggressively = overloadAggressively;
+    }
+
+
+    public void setDefaultpackage(String defaultPackage)
+    {
+        configuration.defaultPackage = ClassUtil.internalClassName(defaultPackage);
+    }
+
+
+    public void setUsemixedcaseclassnames(boolean useMixedCaseClassNames)
+    {
+        configuration.useMixedCaseClassNames = useMixedCaseClassNames;
+    }
+
+
+    public void setRenamesourcefileattribute(String newSourceFileAttribute)
+    {
+        configuration.newSourceFileAttribute = newSourceFileAttribute;
+    }
+
+
+    public void setVerbose(boolean verbose)
+    {
+        configuration.verbose = verbose;
+    }
+
+
+    public void setNote(boolean note)
+    {
+        configuration.note = note;
+    }
+
+
+    public void setWarn(boolean warn)
+    {
+        configuration.warn = warn;
+    }
+
+
+    public void setIgnorewarnings(boolean ignoreWarnings)
+    {
+        configuration.ignoreWarnings = ignoreWarnings;
+    }
+
+
+    public void setDump(File dump)
+    {
+        configuration.dump = optionalFileName(dump);
+    }
+
+
+    // Implementations for Task.
+
+    public void execute() throws BuildException
+    {
+        try
+        {
+            ProGuard proGuard = new ProGuard(configuration);
+            proGuard.execute();
+        }
+        catch (IOException ex)
+        {
+            throw new BuildException(ex.getMessage());
+        }
+    }
+
+
+    // Small utility methods.
+
+    private String optionalFileName(File file)
+    {
+        String fileName = file.getName();
+
+        return
+            fileName.equalsIgnoreCase("false") ||
+            fileName.equalsIgnoreCase("no")    ||
+            fileName.equalsIgnoreCase("off")    ? null :
+            fileName.equalsIgnoreCase("true")  ||
+            fileName.equalsIgnoreCase("yes")   ||
+            fileName.equalsIgnoreCase("on")     ? ""   :
+                                                  file.getPath();
+    }
+}
diff --git a/src/proguard/ant/package.html b/src/proguard/ant/package.html
new file mode 100644
index 0000000..75e0466
--- /dev/null
+++ b/src/proguard/ant/package.html
@@ -0,0 +1,3 @@
+<body>
+This package contains the Ant task for ProGuard.
+</body>
diff --git a/src/proguard/ant/task.properties b/src/proguard/ant/task.properties
new file mode 100644
index 0000000..b676db7
--- /dev/null
+++ b/src/proguard/ant/task.properties
@@ -0,0 +1,2 @@
+proguard              = proguard.ant.ProGuardTask
+proguardconfiguration = proguard.ant.ConfigurationTask
diff --git a/src/proguard/classfile/ClassConstants.java b/src/proguard/classfile/ClassConstants.java
new file mode 100644
index 0000000..844d616
--- /dev/null
+++ b/src/proguard/classfile/ClassConstants.java
@@ -0,0 +1,203 @@
+/* $Id: ClassConstants.java,v 1.24 2004/12/19 21:03:54 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+
+/**
+ * Constants used in representing a Java class file (*.class).
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public interface ClassConstants
+{
+    public static final String CLASS_FILE_EXTENSION = ".class";
+
+    public static final int MAGIC = 0xCAFEBABE;
+
+    public static final int MAJOR_VERSION_MIN = 45;
+    public static final int MINOR_VERSION_MIN = 3;
+    public static final int MAJOR_VERSION_MAX = 49;
+    public static final int MINOR_VERSION_MAX = 0;
+
+    public static final int    INTERNAL_ACC_PUBLIC       = 0x0001;
+    public static final int    INTERNAL_ACC_PRIVATE      = 0x0002;
+    public static final int    INTERNAL_ACC_PROTECTED    = 0x0004;
+    public static final int    INTERNAL_ACC_STATIC       = 0x0008;
+    public static final int    INTERNAL_ACC_FINAL        = 0x0010;
+    public static final int    INTERNAL_ACC_SUPER        = 0x0020;
+    public static final int    INTERNAL_ACC_SYNCHRONIZED = 0x0020;
+    public static final int    INTERNAL_ACC_VOLATILE     = 0x0040;
+    public static final int    INTERNAL_ACC_TRANSIENT    = 0x0080;
+    public static final int    INTERNAL_ACC_BRIDGE       = 0x0040;
+    public static final int    INTERNAL_ACC_VARARGS      = 0x0080;
+    public static final int    INTERNAL_ACC_NATIVE       = 0x0100;
+    public static final int    INTERNAL_ACC_INTERFACE    = 0x0200;
+    public static final int    INTERNAL_ACC_ABSTRACT     = 0x0400;
+    public static final int    INTERNAL_ACC_STRICT       = 0x0800;
+    public static final int    INTERNAL_ACC_SYNTHETIC    = 0x1000;
+    public static final int    INTERNAL_ACC_ANNOTATTION  = 0x2000;
+    public static final int    INTERNAL_ACC_ENUM         = 0x4000;
+
+    public static final int    VALID_INTERNAL_ACC_CLASS  = INTERNAL_ACC_PUBLIC       |
+                                                           INTERNAL_ACC_FINAL        |
+                                                           INTERNAL_ACC_SUPER        |
+                                                           INTERNAL_ACC_INTERFACE    |
+                                                           INTERNAL_ACC_ABSTRACT     |
+                                                           INTERNAL_ACC_SYNTHETIC    |
+                                                           INTERNAL_ACC_ANNOTATTION  |
+                                                           INTERNAL_ACC_ENUM;
+    public static final int    VALID_INTERNAL_ACC_FIELD  = INTERNAL_ACC_PUBLIC       |
+                                                           INTERNAL_ACC_PRIVATE      |
+                                                           INTERNAL_ACC_PROTECTED    |
+                                                           INTERNAL_ACC_STATIC       |
+                                                           INTERNAL_ACC_FINAL        |
+                                                           INTERNAL_ACC_VOLATILE     |
+                                                           INTERNAL_ACC_TRANSIENT    |
+                                                           INTERNAL_ACC_SYNTHETIC    |
+                                                           INTERNAL_ACC_ENUM;
+    public static final int    VALID_INTERNAL_ACC_METHOD = INTERNAL_ACC_PUBLIC       |
+                                                           INTERNAL_ACC_PRIVATE      |
+                                                           INTERNAL_ACC_PROTECTED    |
+                                                           INTERNAL_ACC_STATIC       |
+                                                           INTERNAL_ACC_FINAL        |
+                                                           INTERNAL_ACC_SYNCHRONIZED |
+                                                           INTERNAL_ACC_BRIDGE       |
+                                                           INTERNAL_ACC_VARARGS      |
+                                                           INTERNAL_ACC_NATIVE       |
+                                                           INTERNAL_ACC_ABSTRACT     |
+                                                           INTERNAL_ACC_STRICT       |
+                                                           INTERNAL_ACC_SYNTHETIC;
+
+    public static final String EXTERNAL_ACC_PUBLIC       = "public";
+    public static final String EXTERNAL_ACC_PRIVATE      = "private";
+    public static final String EXTERNAL_ACC_PROTECTED    = "protected";
+    public static final String EXTERNAL_ACC_STATIC       = "static";
+    public static final String EXTERNAL_ACC_FINAL        = "final";
+    public static final String EXTERNAL_ACC_SUPER        = "super";
+    public static final String EXTERNAL_ACC_SYNCHRONIZED = "synchronized";
+    public static final String EXTERNAL_ACC_VOLATILE     = "volatile";
+    public static final String EXTERNAL_ACC_TRANSIENT    = "transient";
+    public static final String EXTERNAL_ACC_NATIVE       = "native";
+    public static final String EXTERNAL_ACC_INTERFACE    = "interface";
+    public static final String EXTERNAL_ACC_ABSTRACT     = "abstract";
+    public static final String EXTERNAL_ACC_STRICT       = "strictfp";
+
+    public static final int CONSTANT_Utf8               = 1;
+    public static final int CONSTANT_Integer            = 3;
+    public static final int CONSTANT_Float              = 4;
+    public static final int CONSTANT_Long               = 5;
+    public static final int CONSTANT_Double             = 6;
+    public static final int CONSTANT_Class              = 7;
+    public static final int CONSTANT_String             = 8;
+    public static final int CONSTANT_Fieldref           = 9;
+    public static final int CONSTANT_Methodref          = 10;
+    public static final int CONSTANT_InterfaceMethodref = 11;
+    public static final int CONSTANT_NameAndType        = 12;
+
+    public static final String ATTR_InnerClasses                         = "InnerClasses";
+    public static final String ATTR_EnclosingMethod                      = "EnclosingMethod";
+    public static final String ATTR_ConstantValue                        = "ConstantValue";
+    public static final String ATTR_Exceptions                           = "Exceptions";
+    public static final String ATTR_Code                                 = "Code";
+    public static final String ATTR_LineNumberTable                      = "LineNumberTable";
+    public static final String ATTR_LocalVariableTable                   = "LocalVariableTable";
+    public static final String ATTR_LocalVariableTypeTable               = "LocalVariableTypeTable";
+    public static final String ATTR_SourceFile                           = "SourceFile";
+    public static final String ATTR_SourceDir                            = "SourceDir";
+    public static final String ATTR_Deprecated                           = "Deprecated";
+    public static final String ATTR_Synthetic                            = "Synthetic";
+    public static final String ATTR_Signature                            = "Signature";
+    public static final String ATTR_RuntimeVisibleAnnotations            = "RuntimeVisibleAnnotations";
+    public static final String ATTR_RuntimeInvisibleAnnotations          = "RuntimeInvisibleAnnotations";
+    public static final String ATTR_RuntimeVisibleParameterAnnotations   = "RuntimeVisibleParameterAnnotations";
+    public static final String ATTR_RuntimeInvisibleParameterAnnotations = "RuntimeInvisibleParameterAnnotations";
+    public static final String ATTR_AnnotationDefault                    = "AnnotationDefault";
+
+    public static final int ELEMENT_VALUE_STRING_CONSTANT = 's';
+    public static final int ELEMENT_VALUE_ENUM_CONSTANT   = 'e';
+    public static final int ELEMENT_VALUE_CLASS           = 'c';
+    public static final int ELEMENT_VALUE_ANNOTATION      = '@';
+    public static final int ELEMENT_VALUE_ARRAY           = '[';
+
+    public static final char EXTERNAL_PACKAGE_SEPARATOR = '.';
+    public static final char INTERNAL_PACKAGE_SEPARATOR = '/';
+
+    public static final char EXTERNAL_METHOD_ARGUMENTS_OPEN      = '(';
+    public static final char EXTERNAL_METHOD_ARGUMENTS_CLOSE     = ')';
+    public static final char EXTERNAL_METHOD_ARGUMENTS_SEPARATOR = ',';
+
+    public static final char INTERNAL_METHOD_ARGUMENTS_OPEN      = '(';
+    public static final char INTERNAL_METHOD_ARGUMENTS_CLOSE     = ')';
+
+    public static final String INTERNAL_NAME_JAVA_LANG_OBJECT     = "java/lang/Object";
+    public static final String INTERNAL_TYPE_JAVA_LANG_OBJECT     = "Ljava/lang/Object;";
+    public static final String INTERNAL_PACKAGE_JAVA_LANG         = "java/lang/";
+
+    public static final String INTERNAL_NAME_JAVA_LANG_CLONEABLE  = "java/lang/Cloneable";
+    public static final String INTERNAL_NAME_JAVA_IO_SERIALIZABLE = "java/io/Serializable";
+
+    public static final String INTERNAL_METHOD_NAME_INIT   = "<init>";
+    public static final String INTERNAL_METHOD_TYPE_INIT   = "()V";
+    public static final String INTERNAL_METHOD_NAME_CLINIT = "<clinit>";
+    public static final String INTERNAL_METHOD_TYPE_CLINIT = "()V";
+
+    public static final String INTERNAL_CLASS_NAME_JAVA_LANG_CLASS = "java/lang/Class";
+    public static final String INTERNAL_METHOD_NAME_CLASS_FOR_NAME = "forName";
+    public static final String INTERNAL_METHOD_TYPE_CLASS_FOR_NAME = "(Ljava/lang/String;)Ljava/lang/Class;";
+
+    public static final String INTERNAL_METHOD_NAME_DOT_CLASS       = "class$";
+    public static final String INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC = "(Ljava/lang/String;)Ljava/lang/Class;";
+    public static final String INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES = "(Ljava/lang/String;Z)Ljava/lang/Class;";
+
+    public static final String INTERNAL_METHOD_NAME_NEW_INSTANCE = "newInstance";
+    public static final String INTERNAL_METHOD_TYPE_NEW_INSTANCE = "()Ljava/lang/Object;";
+
+    public static final char   INTERNAL_TYPE_VOID          = 'V';
+    public static final char   INTERNAL_TYPE_BOOLEAN       = 'Z';
+    public static final char   INTERNAL_TYPE_BYTE          = 'B';
+    public static final char   INTERNAL_TYPE_CHAR          = 'C';
+    public static final char   INTERNAL_TYPE_SHORT         = 'S';
+    public static final char   INTERNAL_TYPE_INT           = 'I';
+    public static final char   INTERNAL_TYPE_FLOAT         = 'F';
+    public static final char   INTERNAL_TYPE_LONG          = 'J';
+    public static final char   INTERNAL_TYPE_DOUBLE        = 'D';
+    public static final char   INTERNAL_TYPE_CLASS_START   = 'L';
+    public static final char   INTERNAL_TYPE_CLASS_END     = ';';
+    public static final char   INTERNAL_TYPE_ARRAY         = '[';
+    public static final char   INTERNAL_TYPE_GENERIC_START = '<';
+    public static final char   INTERNAL_TYPE_GENERIC_END   = '>';
+
+    public static final String EXTERNAL_TYPE_JAVA_LANG_OBJECT = "java.lang.Object";
+    public static final String EXTERNAL_PACKAGE_JAVA_LANG     = "java.lang.";
+
+    public static final String EXTERNAL_TYPE_VOID        = "void";
+    public static final String EXTERNAL_TYPE_BOOLEAN     = "boolean";
+    public static final String EXTERNAL_TYPE_BYTE        = "byte";
+    public static final String EXTERNAL_TYPE_CHAR        = "char";
+    public static final String EXTERNAL_TYPE_SHORT       = "short";
+    public static final String EXTERNAL_TYPE_INT         = "int";
+    public static final String EXTERNAL_TYPE_FLOAT       = "float";
+    public static final String EXTERNAL_TYPE_LONG        = "long";
+    public static final String EXTERNAL_TYPE_DOUBLE      = "double";
+    public static final String EXTERNAL_TYPE_ARRAY       = "[]";
+}
diff --git a/src/proguard/classfile/ClassCpInfo.java b/src/proguard/classfile/ClassCpInfo.java
new file mode 100644
index 0000000..3c8c573
--- /dev/null
+++ b/src/proguard/classfile/ClassCpInfo.java
@@ -0,0 +1,123 @@
+/* $Id: ClassCpInfo.java,v 1.20 2004/11/14 00:54:38 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+
+/**
+ * Representation of a 'class' entry in the ConstantPool.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class ClassCpInfo extends CpInfo
+{
+    public int u2nameIndex;
+
+    /**
+     * An extra field pointing to the referenced ClassFile object.
+     * This field is filled out by the <code>{@link
+     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
+     */
+    public ClassFile referencedClassFile;
+
+
+    protected ClassCpInfo()
+    {
+    }
+
+
+    /**
+     * Creates a new ClassCpInfo with the given name index.
+     * @param u2nameIndex         the index of the name in the constant pool.
+     * @param referencedClassFile the referenced class file.
+     */
+    public ClassCpInfo(int       u2nameIndex,
+                       ClassFile referencedClassFile)
+    {
+        this.u2nameIndex         = u2nameIndex;
+        this.referencedClassFile = referencedClassFile;
+    }
+
+
+    /**
+     * Returns the name index.
+     */
+    protected int getNameIndex()
+    {
+        return u2nameIndex;
+    }
+
+    /**
+     * Sets the name index.
+     */
+    protected void setNameIndex(int index)
+    {
+        u2nameIndex = index;
+    }
+
+
+    /**
+     * Returns the name.
+     */
+    public String getName(ClassFile classFile)
+    {
+        return classFile.getCpString(u2nameIndex);
+    }
+
+
+    // Implementations for CpInfo.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_Class;
+    }
+
+    protected void readInfo(DataInput din) throws IOException
+    {
+        u2nameIndex = din.readUnsignedShort();
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2nameIndex);
+    }
+
+    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
+    {
+        cpInfoVisitor.visitClassCpInfo(classFile, this);
+    }
+
+
+    /**
+     * Lets the referenced class file accept the given visitor.
+     */
+    public void referencedClassAccept(ClassFileVisitor classFileVisitor)
+    {
+        if (referencedClassFile != null)
+        {
+            referencedClassFile.accept(classFileVisitor);
+        }
+    }
+}
diff --git a/src/proguard/classfile/ClassFile.java b/src/proguard/classfile/ClassFile.java
new file mode 100644
index 0000000..e9f6b7e
--- /dev/null
+++ b/src/proguard/classfile/ClassFile.java
@@ -0,0 +1,188 @@
+/* $Id: ClassFile.java,v 1.20 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.*;
+
+
+/**
+ * This interface provides access to the data in a Java class file (*.class).
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public interface ClassFile extends VisitorAccepter
+{
+    /**
+     * Returns the access flags of this class.
+     * @see ClassConstants
+     */
+    public int getAccessFlags();
+
+    /**
+     * Returns the full internal name of this class.
+     */
+    public String getName();
+
+    /**
+     * Returns the full internal name of the super class of this class, or
+     * null if this class represents java.lang.Object.
+     */
+    public String getSuperName();
+
+    /**
+     * Returns the full internal name of the interface at the given index of
+     * this class.
+     */
+    public String getInterfaceName(int index);
+
+    /**
+     * Returns the tag value of the CpEntry at the specified index.
+     */
+    public int getCpTag(int cpIndex);
+
+    /**
+     * Returns the String value of the Utf8CpEntry at the specified index.
+     */
+    public String getCpString(int cpIndex);
+
+    /**
+     * Returns the class name of ClassCpEntry at the specified index.
+     */
+    public String getCpClassNameString(int cpIndex);
+
+    /**
+     * Returns the name of the NameAndTypeCpEntry at the specified index.
+     */
+    public String getCpNameString(int cpIndex);
+
+    /**
+     * Returns the type of the NameAndTypeCpEntry at the specified index.
+     */
+    public String getCpTypeString(int cpIndex);
+
+
+    // Methods pertaining to related class files.
+
+    /**
+     * Notifies this ClassFile that it is being subclassed by another class.
+     */
+    public void addSubClass(ClassFile classFile);
+
+    /**
+     * Returns the super class of this class.
+     */
+    public ClassFile getSuperClass();
+
+    /**
+     * Returns the interface at the given index.
+     */
+    public ClassFile getInterface(int index);
+
+    /**
+     * Returns whether this class extends the given class.
+     * A class is always considered to extend itself.
+     * Interfaces are considered to only extend the root Object class.
+     */
+    public boolean extends_(ClassFile classFile);
+
+    /**
+     * Returns whether this class implements the given class.
+     * A class is always considered to implement itself.
+     * Interfaces are considered to implement all their superinterfaces.
+     */
+    public boolean implements_(ClassFile classFile);
+
+
+    // Methods for getting specific class members.
+
+    /**
+     * Returns the field with the given name and descriptor.
+     */
+    FieldInfo findField(String name, String descriptor);
+
+    /**
+     * Returns the method with the given name and descriptor.
+     */
+    MethodInfo findMethod(String name, String descriptor);
+
+
+    // Methods for accepting various types of visitors.
+
+    /**
+     * Accepts the given class file visitor.
+     */
+    public void accept(ClassFileVisitor classFileVisitor);
+
+    /**
+     * Accepts the given class file visitor in the class hierarchy.
+     * @param visitThisClass   specifies whether to visit this class.
+     * @param visitSuperClass  specifies whether to visit the super classes.
+     * @param visitInterfaces  specifies whether to visit the interfaces.
+     * @param visitSubclasses  specifies whether to visit the subclasses.
+     * @param classFileVisitor the <code>ClassFileVisitor</code> that will
+     *                         visit the class hierarchy.
+     */
+    public void hierarchyAccept(boolean          visitThisClass,
+                                boolean          visitSuperClass,
+                                boolean          visitInterfaces,
+                                boolean          visitSubclasses,
+                                ClassFileVisitor classFileVisitor);
+
+    /**
+     * Lets the given constant pool entry visitor visit all constant pool entries
+     * of this class.
+     */
+    public void constantPoolEntriesAccept(CpInfoVisitor cpInfoVisitor);
+
+    /**
+     * Lets the given constant pool entry visitor visit the constant pool entry
+     * at the specified index.
+     */
+    public void constantPoolEntryAccept(int index, CpInfoVisitor cpInfoVisitor);
+
+    /**
+     * Lets the given member info visitor visit all fields of this class.
+     */
+    public void fieldsAccept(MemberInfoVisitor memberInfoVisitor);
+
+    /**
+     * Lets the given member info visitor visit the specified field.
+     */
+    public void fieldAccept(String name, String descriptor, MemberInfoVisitor memberInfoVisitor);
+
+    /**
+     * Lets the given member info visitor visit all methods of this class.
+     */
+    public void methodsAccept(MemberInfoVisitor memberInfoVisitor);
+
+    /**
+     * Lets the given member info visitor visit the specified method.
+     */
+    public void methodAccept(String name, String descriptor, MemberInfoVisitor memberInfoVisitor);
+
+    /**
+     * Lets the given attribute info visitor visit all attributes of this class.
+     */
+    public void attributesAccept(AttrInfoVisitor attrInfoVisitor);
+}
diff --git a/src/proguard/classfile/ClassPool.java b/src/proguard/classfile/ClassPool.java
new file mode 100644
index 0000000..e60728e
--- /dev/null
+++ b/src/proguard/classfile/ClassPool.java
@@ -0,0 +1,151 @@
+/* $Id: ClassPool.java,v 1.16 2004/10/23 16:53:00 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+
+import java.util.*;
+
+/**
+ * This is a set of representations of class files. They can be enumerated or
+ * retrieved by name. They can also be accessed by means of class file visitors.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassPool
+{
+    private Map classFiles = new HashMap();
+
+
+    /**
+     * Adds the given ClassFile to the class pool. If a class file of the same
+     * name is already present, it is left unchanged and the old class file is
+     * returned.
+     */
+    public ClassFile addClass(ClassFile classFile)
+    {
+        String name = classFile.getName();
+
+        ClassFile previousClassFile = (ClassFile)classFiles.put(name, classFile);
+        if (previousClassFile != null)
+        {
+            // We'll put the original one back.
+            classFiles.put(name, previousClassFile);
+        }
+
+        return previousClassFile;
+    }
+
+
+    /**
+     * Removes the given ClassFile from the class pool.
+     */
+    public void removeClass(ClassFile classFile)
+    {
+        classFiles.remove(classFile.getName());
+    }
+
+
+    /**
+     * Returns a ClassFile from the class pool based on its name. Returns
+     * <code>null</code> if the class with the given name is not in the class
+     * pool. Returns the base class if the class name is an array type, and the
+     * <code>java.lang.Object</code> class if that base class is a primitive type.
+     */
+    public ClassFile getClass(String className)
+    {
+        return (ClassFile)classFiles.get(ClassUtil.internalClassNameFromType(className));
+    }
+
+
+    /**
+     * Returns an Iterator of all ClassFile objects in the class pool.
+     */
+    public Iterator elements()
+    {
+        return classFiles.values().iterator();
+    }
+
+
+    /**
+     * Returns the number of class files in the class pool.
+     */
+    public int size()
+    {
+        return classFiles.size();
+    }
+
+
+    /**
+     * Applies the given ClassPoolVisitor to the class pool.
+     */
+    public void accept(ClassPoolVisitor classPoolVisitor)
+    {
+        classPoolVisitor.visitClassPool(this);
+    }
+
+
+    /**
+     * Applies the given ClassFileVisitor to all classes in the class pool,
+     * in random order.
+     */
+    public void classFilesAccept(ClassFileVisitor classFileVisitor)
+    {
+        Iterator iterator = elements();
+        while (iterator.hasNext())
+        {
+            ClassFile classFile = (ClassFile)iterator.next();
+            classFile.accept(classFileVisitor);
+        }
+    }
+
+
+    /**
+     * Applies the given ClassFileVisitor to all classes in the class pool,
+     * in sorted order.
+     */
+    public void classFilesAcceptAlphabetically(ClassFileVisitor classFileVisitor)
+    {
+        TreeMap sortedClassFiles = new TreeMap(classFiles);
+        Iterator iterator = sortedClassFiles.values().iterator();
+        while (iterator.hasNext())
+        {
+            ClassFile classFile = (ClassFile)iterator.next();
+            classFile.accept(classFileVisitor);
+        }
+    }
+
+
+    /**
+     * Applies the given ClassFileVisitor to the class with the given name,
+     * if it is present in the class pool.
+     */
+    public void classFileAccept(ClassFileVisitor classFileVisitor, String className)
+    {
+        ClassFile classFile = getClass(className);
+        if (classFile != null)
+        {
+            classFile.accept(classFileVisitor);
+        }
+    }
+}
diff --git a/src/proguard/classfile/CpInfo.java b/src/proguard/classfile/CpInfo.java
new file mode 100644
index 0000000..81c3cbe
--- /dev/null
+++ b/src/proguard/classfile/CpInfo.java
@@ -0,0 +1,166 @@
+/* $Id: CpInfo.java,v 1.22 2004/10/23 16:53:00 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+
+/**
+ * Representation of an entry in the ConstantPool. Specific types of entry
+ * have their representations sub-classed from this.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public abstract class CpInfo implements VisitorAccepter
+{
+    // Shared copies of constant pool objects, to avoid creating a lot of objects.
+    private static final IntegerCpInfo            integerCpInfo            = new IntegerCpInfo();
+    private static final FloatCpInfo              floatCpInfo              = new FloatCpInfo();
+    private static final LongCpInfo               longCpInfo               = new LongCpInfo();
+    private static final DoubleCpInfo             doubleCpInfo             = new DoubleCpInfo();
+    private static final StringCpInfo             stringCpInfo             = new StringCpInfo();
+    private static final FieldrefCpInfo           fieldrefCpInfo           = new FieldrefCpInfo();
+    private static final MethodrefCpInfo          methodrefCpInfo          = new MethodrefCpInfo();
+    private static final InterfaceMethodrefCpInfo interfaceMethodrefCpInfo = new InterfaceMethodrefCpInfo();
+    private static final NameAndTypeCpInfo        nameAndTypeCpInfo        = new NameAndTypeCpInfo();
+
+
+    //public int  u1tag;
+    //public byte info[];
+
+    /**
+     * An extra field in which visitors can store information.
+     */
+    public Object visitorInfo;
+
+
+    /**
+     * Creates a new CpInfo from the data passed.
+     *
+     * @throws IOException if the class file is corrupt or incomplete.
+     */
+    public static CpInfo create(DataInput din) throws IOException
+    {
+        // Instantiate based on tag byte
+        CpInfo cpInfo = null;
+        int u1tag = din.readUnsignedByte();
+        switch (u1tag)
+        {
+        case ClassConstants.CONSTANT_Utf8:               cpInfo = new Utf8CpInfo();              break;
+        case ClassConstants.CONSTANT_Integer:            cpInfo = new IntegerCpInfo();           break;
+        case ClassConstants.CONSTANT_Float:              cpInfo = new FloatCpInfo();             break;
+        case ClassConstants.CONSTANT_Long:               cpInfo = new LongCpInfo();              break;
+        case ClassConstants.CONSTANT_Double:             cpInfo = new DoubleCpInfo();            break;
+        case ClassConstants.CONSTANT_Class:              cpInfo = new ClassCpInfo();             break;
+        case ClassConstants.CONSTANT_String:             cpInfo = new StringCpInfo();            break;
+        case ClassConstants.CONSTANT_Fieldref:           cpInfo = new FieldrefCpInfo();          break;
+        case ClassConstants.CONSTANT_Methodref:          cpInfo = new MethodrefCpInfo();         break;
+        case ClassConstants.CONSTANT_InterfaceMethodref: cpInfo = new InterfaceMethodrefCpInfo();break;
+        case ClassConstants.CONSTANT_NameAndType:        cpInfo = new NameAndTypeCpInfo();       break;
+        default: throw new IOException("Unknown constant type ["+u1tag+"] in constant pool");
+        }
+        cpInfo.readInfo(din);
+        return cpInfo;
+    }
+
+
+    /**
+     * Creates a new CpInfo from the data passed, for UTF-8 and Class constant
+     * pool entries, or returns a shared object, for all other entries.
+     *
+     * @throws IOException if the class file is corrupt or incomplete.
+     */
+    public static CpInfo createOrShare(DataInput din) throws IOException
+    {
+        // Instantiate based on tag byte
+        CpInfo cpInfo = null;
+        int u1tag = din.readUnsignedByte();
+        switch (u1tag)
+        {
+        case ClassConstants.CONSTANT_Utf8:               cpInfo = new Utf8CpInfo();         break;
+        case ClassConstants.CONSTANT_Integer:            cpInfo = integerCpInfo;            break;
+        case ClassConstants.CONSTANT_Float:              cpInfo = floatCpInfo;              break;
+        case ClassConstants.CONSTANT_Long:               cpInfo = longCpInfo;               break;
+        case ClassConstants.CONSTANT_Double:             cpInfo = doubleCpInfo;             break;
+        case ClassConstants.CONSTANT_Class:              cpInfo = new ClassCpInfo();        break;
+        case ClassConstants.CONSTANT_String:             cpInfo = stringCpInfo;             break;
+        case ClassConstants.CONSTANT_Fieldref:           cpInfo = fieldrefCpInfo;           break;
+        case ClassConstants.CONSTANT_Methodref:          cpInfo = methodrefCpInfo;          break;
+        case ClassConstants.CONSTANT_InterfaceMethodref: cpInfo = interfaceMethodrefCpInfo; break;
+        case ClassConstants.CONSTANT_NameAndType:        cpInfo = nameAndTypeCpInfo;        break;
+        default: throw new IOException("Unknown constant type ["+u1tag+"] in constant pool");
+        }
+        cpInfo.readInfo(din);
+        return cpInfo;
+    }
+
+
+    /**
+     * Exports the representation to a DataOutput stream.
+     */
+    public void write(DataOutput dout) throws IOException
+    {
+        dout.writeByte(getTag());
+        writeInfo(dout);
+    }
+
+
+    // Abstract methods to be implemented by extensions.
+
+    /**
+     * Returns the class pool info tag that specifies the entry type.
+     */
+    public abstract int getTag();
+
+
+    /**
+     * Reads the 'info' data following the u1tag byte.
+     */
+    protected abstract void readInfo(DataInput din) throws IOException;
+
+
+    /**
+     * Writes the 'info' data following the u1tag byte.
+     */
+    protected abstract void writeInfo(DataOutput dout) throws IOException;
+
+
+    /**
+     * Accepts the given visitor.
+     */
+    public abstract void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor);
+
+
+    // Implementations for VisitorAccepter.
+
+    public Object getVisitorInfo()
+    {
+        return visitorInfo;
+    }
+
+    public void setVisitorInfo(Object visitorInfo)
+    {
+        this.visitorInfo = visitorInfo;
+    }
+}
diff --git a/src/proguard/classfile/DoubleCpInfo.java b/src/proguard/classfile/DoubleCpInfo.java
new file mode 100644
index 0000000..b309649
--- /dev/null
+++ b/src/proguard/classfile/DoubleCpInfo.java
@@ -0,0 +1,97 @@
+/* $Id: DoubleCpInfo.java,v 1.17 2004/10/23 16:53:00 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+
+/**
+ * Representation of a 'double' entry in the ConstantPool (takes up two indices).
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class DoubleCpInfo extends CpInfo
+{
+    public int u4highBytes;
+    public int u4lowBytes;
+
+
+    /**
+     * Creates a new DoubleCpInfo with the given double value.
+     */
+    public DoubleCpInfo(double value)
+    {
+        setValue(value);
+    }
+
+
+    protected DoubleCpInfo()
+    {
+    }
+
+
+    /**
+     * Returns the double value of this DoubleCpInfo.
+     */
+    public double getValue()
+    {
+        return Double.longBitsToDouble(((long)u4highBytes << 32) | (long)u4lowBytes);
+    }
+
+
+    /**
+     * Sets the double value of this DoubleCpInfo.
+     */
+    public void setValue(double value)
+    {
+        long longValue = Double.doubleToLongBits(value);
+        u4highBytes = (int)(longValue >> 32);
+        u4lowBytes  = (int) longValue;
+    }
+
+
+    // Implementations for CpInfo.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_Double;
+    }
+
+    protected void readInfo(DataInput din) throws IOException
+    {
+        u4highBytes = din.readInt();
+        u4lowBytes = din.readInt();
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeInt(u4highBytes);
+        dout.writeInt(u4lowBytes);
+    }
+
+    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
+    {
+        cpInfoVisitor.visitDoubleCpInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/FieldInfo.java b/src/proguard/classfile/FieldInfo.java
new file mode 100644
index 0000000..9c7607b
--- /dev/null
+++ b/src/proguard/classfile/FieldInfo.java
@@ -0,0 +1,33 @@
+/* $Id: FieldInfo.java,v 1.12 2004/10/23 16:53:00 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+
+
+/**
+ * Representation of a field from a class file.
+ *
+ * @author Eric Lafortune
+ */
+public interface FieldInfo extends MemberInfo
+{
+}
diff --git a/src/proguard/classfile/FieldrefCpInfo.java b/src/proguard/classfile/FieldrefCpInfo.java
new file mode 100644
index 0000000..1ff608b
--- /dev/null
+++ b/src/proguard/classfile/FieldrefCpInfo.java
@@ -0,0 +1,69 @@
+/* $Id: FieldrefCpInfo.java,v 1.20 2004/10/23 16:53:00 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+
+/**
+ * Representation of a 'field reference' entry in the ConstantPool.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class FieldrefCpInfo extends RefCpInfo
+{
+    protected FieldrefCpInfo()
+    {
+    }
+
+
+    /**
+     * Creates a new FieldrefCpInfo with the given name and type indices.
+     * @param u2classIndex         the index of the class in the constant pool.
+     * @param u2nameAndTypeIndex   the index of the name and type entry in the constant pool.
+     * @param referencedClassFile  the referenced class file.
+     * @param referencedMemberInfo the referenced member info.
+     */
+    public FieldrefCpInfo(int        u2classIndex,
+                          int        u2nameAndTypeIndex,
+                          ClassFile  referencedClassFile,
+                          MemberInfo referencedMemberInfo)
+    {
+        this.u2classIndex         = u2classIndex;
+        this.u2nameAndTypeIndex   = u2nameAndTypeIndex;
+        this.referencedClassFile  = referencedClassFile;
+        this.referencedMemberInfo = referencedMemberInfo;
+    }
+
+
+    // Implementations for CpInfo.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_Fieldref;
+    }
+
+    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
+    {
+        cpInfoVisitor.visitFieldrefCpInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/FloatCpInfo.java b/src/proguard/classfile/FloatCpInfo.java
new file mode 100644
index 0000000..2218446
--- /dev/null
+++ b/src/proguard/classfile/FloatCpInfo.java
@@ -0,0 +1,92 @@
+/* $Id: FloatCpInfo.java,v 1.16 2004/10/23 16:53:00 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+
+/**
+ * Representation of a 'float' entry in the ConstantPool.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class FloatCpInfo extends CpInfo
+{
+    public int u4bytes;
+
+
+    /**
+     * Creates a new FloatCpInfo with the given float value.
+     */
+    public FloatCpInfo(float value)
+    {
+        setValue(value);
+    }
+
+
+    protected FloatCpInfo()
+    {
+    }
+
+
+    /**
+     * Returns the float value of this FloatCpInfo.
+     */
+    public float getValue()
+    {
+        return Float.intBitsToFloat(u4bytes);
+    }
+
+
+    /**
+     * Sets the float value of this FloatCpInfo.
+     */
+    public void setValue(float value)
+    {
+        u4bytes = Float.floatToIntBits(value);
+    }
+
+
+    // Implementations for CpInfo.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_Float;
+    }
+
+    protected void readInfo(DataInput din) throws IOException
+    {
+        u4bytes = din.readInt();
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeInt(u4bytes);
+    }
+
+    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
+    {
+        cpInfoVisitor.visitFloatCpInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/IntegerCpInfo.java b/src/proguard/classfile/IntegerCpInfo.java
new file mode 100644
index 0000000..eec5f50
--- /dev/null
+++ b/src/proguard/classfile/IntegerCpInfo.java
@@ -0,0 +1,92 @@
+/* $Id: IntegerCpInfo.java,v 1.16 2004/10/23 16:53:00 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+
+/**
+ * Representation of a 'integer' entry in the ConstantPool.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class IntegerCpInfo extends CpInfo
+{
+    public int u4bytes;
+
+
+    /**
+     * Creates a new IntegerCpInfo with the given integer value.
+     */
+    public IntegerCpInfo(int value)
+    {
+        setValue(value);
+    }
+
+
+    protected IntegerCpInfo()
+    {
+    }
+
+
+    /**
+     * Returns the integer value of this IntegerCpInfo.
+     */
+    public int getValue()
+    {
+        return u4bytes;
+    }
+
+
+    /**
+     * Sets the integer value of this IntegerCpInfo.
+     */
+    public void setValue(int value)
+    {
+        u4bytes = value;
+    }
+
+
+    // Implementations for CpInfo.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_Integer;
+    }
+
+    protected void readInfo(DataInput din) throws IOException
+    {
+        u4bytes = din.readInt();
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeInt(u4bytes);
+    }
+
+    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
+    {
+        cpInfoVisitor.visitIntegerCpInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/InterfaceMethodrefCpInfo.java b/src/proguard/classfile/InterfaceMethodrefCpInfo.java
new file mode 100644
index 0000000..a9c229b
--- /dev/null
+++ b/src/proguard/classfile/InterfaceMethodrefCpInfo.java
@@ -0,0 +1,69 @@
+/* $Id: InterfaceMethodrefCpInfo.java,v 1.20 2004/11/07 18:49:09 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+
+/**
+ * Representation of a 'interface method reference' entry in the ConstantPool.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class InterfaceMethodrefCpInfo extends RefCpInfo
+{
+    protected InterfaceMethodrefCpInfo()
+    {
+    }
+
+
+    /**
+     * Creates a new InterfaceMethodrefCpInfo with the given name and type indices.
+     * @param u2classIndex         the index of the class in the constant pool.
+     * @param u2nameAndTypeIndex   the index of the name and type entry in the constant pool.
+     * @param referencedClassFile  the referenced class file.
+     * @param referencedMemberInfo the referenced member info.
+     */
+    public InterfaceMethodrefCpInfo(int        u2classIndex,
+                                    int        u2nameAndTypeIndex,
+                                    ClassFile  referencedClassFile,
+                                    MemberInfo referencedMemberInfo)
+    {
+        this.u2classIndex         = u2classIndex;
+        this.u2nameAndTypeIndex   = u2nameAndTypeIndex;
+        this.referencedClassFile  = referencedClassFile;
+        this.referencedMemberInfo = referencedMemberInfo;
+    }
+
+
+    // Implementations for CpInfo.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_InterfaceMethodref;
+    }
+
+    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
+    {
+        cpInfoVisitor.visitInterfaceMethodrefCpInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/LibraryClassFile.java b/src/proguard/classfile/LibraryClassFile.java
new file mode 100644
index 0000000..1f6af92
--- /dev/null
+++ b/src/proguard/classfile/LibraryClassFile.java
@@ -0,0 +1,586 @@
+/* $Id: LibraryClassFile.java,v 1.35 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.*;
+
+import java.io.*;
+
+/**
+ * This is a compact representation of the essential data in a Java class file.
+ * A LibraryClassFile instance representing a *.class file can be generated
+ * using the static create(DataInput) method, but not persisted back.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class LibraryClassFile implements ClassFile
+{
+    // Some objects and arrays that can be reused.
+    private static LibraryClassFile    reusableLibraryClassFile;
+    private static CpInfo[]            reusableConstantPool;
+    private static LibraryFieldInfo[]  reusableFields;
+    private static LibraryMethodInfo[] reusableMethods;
+
+
+    public int                 u2accessFlags;
+    public String              thisClassName;
+    public String              superClassName;
+    public String[]            interfaceNames;
+    public LibraryFieldInfo[]  fields;
+    public LibraryMethodInfo[] methods;
+
+    /**
+     * An extra field pointing to the superclass of this class.
+     * This field is filled out by the <code>{@link ClassFileReferenceInitializer}</code>.
+     */
+    public ClassFile   superClass          = null;
+
+    /**
+     * An extra field pointing to the interfaces of this class.
+     * This field is filled out by the <code>{@link ClassFileReferenceInitializer}</code>.
+     */
+    public ClassFile[] interfaceClasses    = null;
+
+    /**
+     * An extra field pointing to the subclasses of this class.
+     * This field is filled out by the <code>{@link ClassFileReferenceInitializer}</code>.
+     */
+    public ClassFile[] subClasses          = null;
+
+    /**
+     * An extra field in which visitors can store information.
+     */
+    public Object visitorInfo;
+
+
+    /**
+     * Creates a new LibraryClassFile from the class file format data in the DataInput
+     * stream. If specified, this method may return <code>null</code> if the
+     * class file is not visible.
+     *
+     * @throws IOException if the class file is corrupt or incomplete
+     */
+    public static LibraryClassFile create(DataInput din,
+                                          boolean skipNonPublicClasses,
+                                          boolean skipNonPublicClassMembers)
+    throws IOException
+    {
+        // See if we have to create a new library class file object.
+        if (reusableLibraryClassFile == null)
+        {
+            reusableLibraryClassFile = new LibraryClassFile();
+        }
+
+        // We'll start using the reusable object.
+        LibraryClassFile libraryClassFile = reusableLibraryClassFile;
+
+        libraryClassFile.read(din, skipNonPublicClasses, skipNonPublicClassMembers);
+
+        // Did we actually read a useful library class file?
+        if (libraryClassFile.thisClassName != null)
+        {
+            // We can't reuse this library class file object next time.
+            reusableLibraryClassFile = null;
+        }
+        else
+        {
+            // We don't have a useful library class file to return.
+            libraryClassFile = null;
+        }
+
+        return libraryClassFile;
+    }
+
+
+    /**
+     * Creates an empty LibraryClassFile.
+     */
+    private LibraryClassFile() {}
+
+
+    /**
+     * Imports the class data into this LibraryClassFile.
+     */
+    private void read(DataInput din,
+                      boolean skipNonPublicClasses,
+                      boolean skipNonPublicClassmembers) throws IOException
+    {
+        // Read and check the class file magic number.
+        int u4magic = din.readInt();
+        ClassUtil.checkMagicNumber(u4magic);
+
+        // Read and check the class file version numbers.
+        int u2minorVersion = din.readUnsignedShort();
+        int u2majorVersion = din.readUnsignedShort();
+        ClassUtil.checkVersionNumbers(u2majorVersion, u2minorVersion);
+
+        // Read the constant pool.
+        int u2constantPoolCount = din.readUnsignedShort();
+
+        // Make sure there's sufficient space in the reused constant pool array.
+        if (reusableConstantPool == null ||
+            reusableConstantPool.length < u2constantPoolCount)
+        {
+            reusableConstantPool = new CpInfo[u2constantPoolCount];
+        }
+
+        // Fill the constant pool. The zero entry is not used, nor are the
+        // entries following a Long or Double.
+        for (int i = 1; i < u2constantPoolCount; i++)
+        {
+            reusableConstantPool[i] = CpInfo.createOrShare(din);
+            int tag = reusableConstantPool[i].getTag();
+            if (tag == ClassConstants.CONSTANT_Long ||
+                tag == ClassConstants.CONSTANT_Double)
+            {
+                i++;
+            }
+        }
+
+        u2accessFlags = din.readUnsignedShort();
+
+        // We may stop parsing this library class file if it's not public anyway.
+        // E.g. only about 60% of all rt.jar classes need to be parsed.
+        if (skipNonPublicClasses && !isVisible())
+        {
+            return;
+        }
+
+        // Read the class and super class indices.
+        int u2thisClass  = din.readUnsignedShort();
+        int u2superClass = din.readUnsignedShort();
+
+        // Store their actual names.
+        thisClassName  = toName(reusableConstantPool, u2thisClass);
+        superClassName = (u2superClass == 0) ? null :
+                         toName(reusableConstantPool, u2superClass);
+
+        // Read the interface indices.
+        int u2interfacesCount = din.readUnsignedShort();
+
+        // Store their actual names.
+        interfaceNames = new String[u2interfacesCount];
+        for (int i = 0; i < u2interfacesCount; i++)
+        {
+            int u2interface = din.readUnsignedShort();
+            interfaceNames[i] = toName(reusableConstantPool, u2interface);
+        }
+
+        // Read the fields.
+        int u2fieldsCount = din.readUnsignedShort();
+
+        // Make sure there's sufficient space in the reused fields array.
+        if (reusableFields == null ||
+            reusableFields.length < u2fieldsCount)
+        {
+            reusableFields = new LibraryFieldInfo[u2fieldsCount];
+        }
+
+        int visibleFieldsCount = 0;
+        for (int i = 0; i < u2fieldsCount; i++)
+        {
+            LibraryFieldInfo field = LibraryFieldInfo.create(din, reusableConstantPool);
+
+            // Only store fields that are visible.
+            if (field.isVisible() ||
+                (!skipNonPublicClassmembers &&
+                 (field.getAccessFlags() & ClassConstants.INTERNAL_ACC_PRIVATE) == 0))
+            {
+                reusableFields[visibleFieldsCount++] = field;
+            }
+        }
+
+        // Copy the visible fields into a fields array of the right size.
+        fields = new LibraryFieldInfo[visibleFieldsCount];
+        System.arraycopy(reusableFields, 0, fields, 0, visibleFieldsCount);
+
+
+        // Read the methods.
+        int u2methodsCount = din.readUnsignedShort();
+
+        // Make sure there's sufficient space in the reused methods array.
+        if (reusableMethods == null ||
+            reusableMethods.length < u2methodsCount)
+        {
+            reusableMethods = new LibraryMethodInfo[u2methodsCount];
+        }
+
+        int visibleMethodsCount = 0;
+        for (int i = 0; i < u2methodsCount; i++)
+        {
+            LibraryMethodInfo method = LibraryMethodInfo.create(din, reusableConstantPool);
+
+            // Only store methods that are visible.
+            if (method.isVisible() ||
+                (!skipNonPublicClasses &&
+                 (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_PRIVATE) == 0))
+            {
+                reusableMethods[visibleMethodsCount++] = method;
+            }
+        }
+
+        // Copy the visible methods into a methods array of the right size.
+        methods = new LibraryMethodInfo[visibleMethodsCount];
+        System.arraycopy(reusableMethods, 0, methods, 0, visibleMethodsCount);
+
+
+        // Skip the attributes.
+        int u2attributesCount = din.readUnsignedShort();
+        for (int i = 0; i < u2attributesCount; i++)
+        {
+            LibraryAttrInfo.skip(din);
+        }
+    }
+
+
+    /**
+     * Returns whether this library class file is visible to the outside world.
+     */
+    boolean isVisible()
+    {
+        return (u2accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0;
+    }
+
+
+    /**
+     * Returns the class name of the ClassCpInfo at the specified index in the
+     * given constant pool.
+     */
+    private String toName(CpInfo[] constantPool, int cpIndex)
+    {
+        ClassCpInfo classEntry = (ClassCpInfo)constantPool[cpIndex];
+        Utf8CpInfo  nameEntry  = (Utf8CpInfo)constantPool[classEntry.getNameIndex()];
+
+        return nameEntry.getString();
+    }
+
+
+    /**
+     * Returns the field with the given name and descriptor.
+     */
+    private LibraryFieldInfo findLibraryField(String name, String descriptor)
+    {
+        for (int i = 0; i < fields.length; i++)
+        {
+            LibraryFieldInfo field = fields[i];
+            if (field != null &&
+                (name       == null || field.getName(this).equals(name)) &&
+                (descriptor == null || field.getDescriptor(this).equals(descriptor)))
+            {
+                return field;
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Returns the method with the given name and descriptor.
+     */
+    private LibraryMethodInfo findLibraryMethod(String name, String descriptor)
+    {
+        for (int i = 0; i < methods.length; i++)
+        {
+            LibraryMethodInfo method = methods[i];
+            if (method != null &&
+                (name       == null || method.getName(this).equals(name)) &&
+                (descriptor == null || method.getDescriptor(this).equals(descriptor)))
+            {
+                return method;
+            }
+        }
+
+        return null;
+    }
+
+
+    // Implementations for ClassFile.
+
+    public int getAccessFlags()
+    {
+        return u2accessFlags;
+    }
+
+    public String getName()
+    {
+        return thisClassName;
+    }
+
+    public String getSuperName()
+    {
+        // This may be java/lang/Object, in which case there is no super.
+        return superClassName;
+    }
+
+    public String getInterfaceName(int index)
+    {
+        return interfaceNames[index];
+    }
+
+    public int getCpTag(int cpIndex)
+    {
+        return -1;
+    }
+
+    public String getCpString(int cpIndex)
+    {
+        return null;
+    }
+
+    public String getCpClassNameString(int cpIndex)
+    {
+        return null;
+    }
+
+    public String getCpNameString(int cpIndex)
+    {
+        return null;
+    }
+
+    public String getCpTypeString(int cpIndex)
+    {
+        return null;
+    }
+
+
+    public void addSubClass(ClassFile classFile)
+    {
+        if (subClasses == null)
+        {
+            subClasses = new ClassFile[1];
+        }
+        else
+        {
+            // Copy the old elements into new larger array.
+            ClassFile[] temp = new ClassFile[subClasses.length+1];
+            System.arraycopy(subClasses, 0, temp, 0, subClasses.length);
+            subClasses = temp;
+        }
+
+        subClasses[subClasses.length-1] = classFile;
+    }
+
+
+    public ClassFile getSuperClass()
+    {
+        return superClass;
+    }
+
+
+    public ClassFile getInterface(int index)
+    {
+        return interfaceClasses[index];
+    }
+
+
+    public boolean extends_(ClassFile classFile)
+    {
+        if (this.equals(classFile))
+        {
+            return true;
+        }
+
+        ClassFile superClass = getSuperClass();
+        return superClass != null &&
+               superClass.extends_(classFile);
+    }
+
+
+    public boolean implements_(ClassFile classFile)
+    {
+        if (this.equals(classFile))
+        {
+            return true;
+        }
+
+        if (interfaceClasses != null)
+        {
+            for (int i = 0; i < interfaceClasses.length; i++)
+            {
+                ClassFile interfaceClass = getInterface(i);
+                if (interfaceClass != null &&
+                    interfaceClass.implements_(classFile))
+                {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+
+    public FieldInfo findField(String name, String descriptor)
+    {
+        return findLibraryField(name, descriptor);
+    }
+
+
+    public MethodInfo findMethod(String name, String descriptor)
+    {
+        return findLibraryMethod(name, descriptor);
+    }
+
+
+    public void accept(ClassFileVisitor classFileVisitor)
+    {
+        classFileVisitor.visitLibraryClassFile(this);
+    }
+
+
+    public void hierarchyAccept(boolean          visitThisClass,
+                                boolean          visitSuperClass,
+                                boolean          visitInterfaces,
+                                boolean          visitSubclasses,
+                                ClassFileVisitor classFileVisitor)
+    {
+        // First visit the current classfile.
+        if (visitThisClass)
+        {
+            accept(classFileVisitor);
+        }
+
+        // Then visit its superclass, recursively.
+        if (visitSuperClass)
+        {
+            if (superClass != null)
+            {
+                superClass.hierarchyAccept(true,
+                                           true,
+                                           visitInterfaces,
+                                           false,
+                                           classFileVisitor);
+            }
+        }
+
+        // Then visit its interfaces, recursively.
+        if (visitInterfaces)
+        {
+            if (interfaceClasses != null)
+            {
+                for (int i = 0; i < interfaceClasses.length; i++)
+                {
+                    ClassFile interfaceClass = getInterface(i);
+                    if (interfaceClass != null)
+                    {
+                        interfaceClass.hierarchyAccept(true,
+                                                       true,
+                                                       true,
+                                                       false,
+                                                       classFileVisitor);
+                    }
+                }
+            }
+        }
+
+        // Then visit its subclasses, recursively.
+        if (visitSubclasses)
+        {
+            if (subClasses != null)
+            {
+                for (int i = 0; i < subClasses.length; i++)
+                {
+                    ClassFile subClass = subClasses[i];
+                    subClass.hierarchyAccept(true,
+                                             false,
+                                             false,
+                                             true,
+                                             classFileVisitor);
+                }
+            }
+        }
+    }
+
+
+    public void constantPoolEntriesAccept(CpInfoVisitor cpInfoVisitor)
+    {
+        // This class doesn't keep references to its constant pool entries.
+    }
+
+    public void constantPoolEntryAccept(int index, CpInfoVisitor cpInfoVisitor)
+    {
+        // This class doesn't keep references to its constant pool entries.
+    }
+
+    public void fieldsAccept(MemberInfoVisitor memberInfoVisitor)
+    {
+        for (int i = 0; i < fields.length; i++)
+        {
+            if (fields[i] != null)
+            {
+                fields[i].accept(this, memberInfoVisitor);
+            }
+        }
+    }
+
+    public void fieldAccept(String name, String descriptor, MemberInfoVisitor memberInfoVisitor)
+    {
+        LibraryMemberInfo libraryMemberInfo = findLibraryField(name, descriptor);
+        if (libraryMemberInfo != null)
+        {
+            libraryMemberInfo.accept(this, memberInfoVisitor);
+        }
+    }
+
+    public void methodsAccept(MemberInfoVisitor memberInfoVisitor)
+    {
+        for (int i = 0; i < methods.length; i++)
+        {
+            if (methods[i] != null)
+            {
+                methods[i].accept(this, memberInfoVisitor);
+            }
+        }
+    }
+
+    public void methodAccept(String name, String descriptor, MemberInfoVisitor memberInfoVisitor)
+    {
+        LibraryMemberInfo libraryMemberInfo = findLibraryMethod(name, descriptor);
+        if (libraryMemberInfo != null)
+        {
+            libraryMemberInfo.accept(this, memberInfoVisitor);
+        }
+    }
+
+    public void attributesAccept(AttrInfoVisitor attrInfoVisitor)
+    {
+        // This class doesn't keep references to its attributes.
+    }
+
+
+    // Implementations for VisitorAccepter.
+
+    public Object getVisitorInfo()
+    {
+        return visitorInfo;
+    }
+
+    public void setVisitorInfo(Object visitorInfo)
+    {
+        this.visitorInfo = visitorInfo;
+    }
+}
diff --git a/src/proguard/classfile/LibraryFieldInfo.java b/src/proguard/classfile/LibraryFieldInfo.java
new file mode 100644
index 0000000..f7f0472
--- /dev/null
+++ b/src/proguard/classfile/LibraryFieldInfo.java
@@ -0,0 +1,60 @@
+/* $Id: LibraryFieldInfo.java,v 1.14 2004/10/23 16:53:00 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+
+/**
+ * Representation of a field from a class-file.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class LibraryFieldInfo extends LibraryMemberInfo implements FieldInfo
+{
+
+
+    /**
+     * Creates a new LibraryFieldInfo from the file format data in the DataInput stream.
+     *
+     * @throws IOException if class file is corrupt or incomplete
+     */
+    public static LibraryFieldInfo create(DataInput din, CpInfo[] constantPool) throws IOException
+    {
+        LibraryFieldInfo fi = new LibraryFieldInfo();
+        fi.read(din, constantPool);
+        return fi;
+    }
+
+    /**
+     * Accepts the given visitor.
+     */
+    public void accept(LibraryClassFile libraryClassFile, MemberInfoVisitor memberInfoVisitor)
+    {
+        memberInfoVisitor.visitLibraryFieldInfo(libraryClassFile, this);
+    }
+
+
+    protected LibraryFieldInfo() {}
+}
diff --git a/src/proguard/classfile/LibraryMemberInfo.java b/src/proguard/classfile/LibraryMemberInfo.java
new file mode 100644
index 0000000..c52b2c2
--- /dev/null
+++ b/src/proguard/classfile/LibraryMemberInfo.java
@@ -0,0 +1,129 @@
+/* $Id: LibraryMemberInfo.java,v 1.21 2004/10/23 16:53:00 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.*;
+
+import java.io.*;
+
+/**
+ * Representation of a field or method from a library class file.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+abstract public class LibraryMemberInfo implements MemberInfo
+{
+    private static final int ACC_VISIBLE = ClassConstants.INTERNAL_ACC_PUBLIC |
+                                           ClassConstants.INTERNAL_ACC_PROTECTED;
+
+
+    public int    u2accessFlags;
+    public String name;
+    public String descriptor;
+
+    /**
+     * An extra field in which visitors can store information.
+     */
+    public Object visitorInfo;
+
+
+    protected LibraryMemberInfo() {}
+
+
+    /**
+     * Accepts the given member info visitor.
+     */
+    public abstract void accept(LibraryClassFile  libraryClassFile,
+                                MemberInfoVisitor memberInfoVisitor);
+
+
+    /**
+     * Imports the field or method data to internal representation.
+     */
+    protected void read(DataInput din, CpInfo[] constantPool) throws IOException
+    {
+        // Read the access flags.
+        u2accessFlags = din.readUnsignedShort();
+
+        // Read the name and descriptor indices.
+        int u2nameIndex       = din.readUnsignedShort();
+        int u2descriptorIndex = din.readUnsignedShort();
+
+        // Store the actual name and descriptor.
+        name       = ((Utf8CpInfo)constantPool[u2nameIndex]).getString();
+        descriptor = ((Utf8CpInfo)constantPool[u2descriptorIndex]).getString();
+
+        // Skip the attributes.
+        int u2attributesCount = din.readUnsignedShort();
+        for (int i = 0; i < u2attributesCount; i++)
+        {
+            LibraryAttrInfo.skip(din);
+        }
+    }
+
+
+    /**
+     * Returns whether this library member is visible to the outside world.
+     */
+    boolean isVisible()
+    {
+        return (u2accessFlags & ACC_VISIBLE) != 0;
+    }
+
+
+    // Implementations for MemberInfo.
+
+    public int getAccessFlags()
+    {
+        return u2accessFlags;
+    }
+
+    public String getName(ClassFile classFile)
+    {
+        return name;
+    }
+
+    public String getDescriptor(ClassFile classFile)
+    {
+        return descriptor;
+    }
+
+    public void accept(ClassFile classFile, MemberInfoVisitor memberInfoVisitor)
+    {
+        accept((LibraryClassFile)classFile, memberInfoVisitor);
+    }
+
+
+    // Implementations for VisitorAccepter.
+
+    public Object getVisitorInfo()
+    {
+        return visitorInfo;
+    }
+
+    public void setVisitorInfo(Object visitorInfo)
+    {
+        this.visitorInfo = visitorInfo;
+    }
+}
diff --git a/src/proguard/classfile/LibraryMethodInfo.java b/src/proguard/classfile/LibraryMethodInfo.java
new file mode 100644
index 0000000..00e4242
--- /dev/null
+++ b/src/proguard/classfile/LibraryMethodInfo.java
@@ -0,0 +1,60 @@
+/* $Id: LibraryMethodInfo.java,v 1.14 2004/10/23 16:53:00 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+
+/**
+ * Representation of a method from a class-file.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class LibraryMethodInfo extends LibraryMemberInfo implements MethodInfo
+{
+
+
+    /**
+     * Creates a new LibraryMethodInfo from the file format data in the DataInput stream.
+     *
+     * @throws IOException if class file is corrupt or incomplete
+     */
+    public static LibraryMethodInfo create(DataInput din, CpInfo[] constantPool) throws IOException
+    {
+        LibraryMethodInfo mi = new LibraryMethodInfo();
+        mi.read(din, constantPool);
+        return mi;
+    }
+
+    /**
+     * Accepts the given visitor.
+     */
+    public void accept(LibraryClassFile libraryClassFile, MemberInfoVisitor memberInfoVisitor)
+    {
+        memberInfoVisitor.visitLibraryMethodInfo(libraryClassFile, this);
+    }
+
+
+    protected LibraryMethodInfo() {}
+}
diff --git a/src/proguard/classfile/LongCpInfo.java b/src/proguard/classfile/LongCpInfo.java
new file mode 100644
index 0000000..9935962
--- /dev/null
+++ b/src/proguard/classfile/LongCpInfo.java
@@ -0,0 +1,96 @@
+/* $Id: LongCpInfo.java,v 1.16 2004/10/23 16:53:00 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+
+/**
+ * Representation of a 'long' entry in the ConstantPool (takes up two indices).
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class LongCpInfo extends CpInfo
+{
+    public int u4highBytes;
+    public int u4lowBytes;
+
+
+    /**
+     * Creates a new LongCpInfo with the given long value.
+     */
+    public LongCpInfo(long value)
+    {
+        setValue(value);
+    }
+
+
+    protected LongCpInfo()
+    {
+    }
+
+
+    /**
+     * Returns the long value of this LongCpInfo.
+     */
+    public long getValue()
+    {
+        return (long)u4highBytes << 32 | (long)u4lowBytes;
+    }
+
+
+    /**
+     * Sets the long value of this LongCpInfo.
+     */
+    public void setValue(long value)
+    {
+        u4highBytes = (int)(value >> 32);
+        u4lowBytes  = (int) value;
+    }
+
+
+    // Implementations for CpInfo.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_Long;
+    }
+
+    protected void readInfo(DataInput din) throws IOException
+    {
+        u4highBytes = din.readInt();
+        u4lowBytes = din.readInt();
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeInt(u4highBytes);
+        dout.writeInt(u4lowBytes);
+    }
+
+    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
+    {
+        cpInfoVisitor.visitLongCpInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/MemberInfo.java b/src/proguard/classfile/MemberInfo.java
new file mode 100644
index 0000000..420f5c2
--- /dev/null
+++ b/src/proguard/classfile/MemberInfo.java
@@ -0,0 +1,53 @@
+/* $Id: MemberInfo.java,v 1.19 2004/10/23 16:53:00 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.MemberInfoVisitor;
+
+/**
+ * Representation of a field or method from a program class file.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public interface MemberInfo extends VisitorAccepter
+{
+    /**
+     * Returns access flags.
+     */
+    public int getAccessFlags();
+
+    /**
+     * Returns method/field string name.
+     */
+    public String getName(ClassFile classFile);
+
+    /**
+     * Returns descriptor string.
+     */
+    public String getDescriptor(ClassFile classFile);
+
+    /**
+     * Accepts the given class file visitor.
+     */
+    public void accept(ClassFile classFile, MemberInfoVisitor memberInfoVisitor);
+}
diff --git a/src/proguard/classfile/MethodInfo.java b/src/proguard/classfile/MethodInfo.java
new file mode 100644
index 0000000..c0bde5a
--- /dev/null
+++ b/src/proguard/classfile/MethodInfo.java
@@ -0,0 +1,33 @@
+/* $Id: MethodInfo.java,v 1.12 2004/10/23 16:53:01 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+
+
+/**
+ * Representation of a method from a class file.
+ *
+ * @author Eric Lafortune
+ */
+public interface MethodInfo extends MemberInfo
+{
+}
diff --git a/src/proguard/classfile/MethodrefCpInfo.java b/src/proguard/classfile/MethodrefCpInfo.java
new file mode 100644
index 0000000..4c317fc
--- /dev/null
+++ b/src/proguard/classfile/MethodrefCpInfo.java
@@ -0,0 +1,69 @@
+/* $Id: MethodrefCpInfo.java,v 1.20 2004/10/23 16:53:01 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+
+/**
+ * Representation of a 'method reference' entry in the ConstantPool.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class MethodrefCpInfo extends RefCpInfo
+{
+    protected MethodrefCpInfo()
+    {
+    }
+
+
+    /**
+     * Creates a new MethodrefCpInfo with the given name and type indices.
+     * @param u2classIndex         the index of the class in the constant pool.
+     * @param u2nameAndTypeIndex   the index of the name and type entry in the constant pool.
+     * @param referencedClassFile  the referenced class file.
+     * @param referencedMemberInfo the referenced member info.
+     */
+    public MethodrefCpInfo(int        u2classIndex,
+                           int        u2nameAndTypeIndex,
+                           ClassFile  referencedClassFile,
+                           MemberInfo referencedMemberInfo)
+    {
+        this.u2classIndex         = u2classIndex;
+        this.u2nameAndTypeIndex   = u2nameAndTypeIndex;
+        this.referencedClassFile  = referencedClassFile;
+        this.referencedMemberInfo = referencedMemberInfo;
+    }
+
+
+    // Implementations for CpInfo.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_Methodref;
+    }
+
+    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
+    {
+        cpInfoVisitor.visitMethodrefCpInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/NameAndTypeCpInfo.java b/src/proguard/classfile/NameAndTypeCpInfo.java
new file mode 100644
index 0000000..b10404d
--- /dev/null
+++ b/src/proguard/classfile/NameAndTypeCpInfo.java
@@ -0,0 +1,162 @@
+/* $Id: NameAndTypeCpInfo.java,v 1.19 2004/10/23 16:53:01 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+
+/**
+ * Representation of a 'name and type' entry in the ConstantPool.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class NameAndTypeCpInfo extends CpInfo implements Cloneable
+{
+    public int u2nameIndex;
+    public int u2descriptorIndex;
+
+    /**
+     * An extra field pointing to the ClassFile objects referenced in the
+     * descriptor string. This field is filled out by the <code>{@link
+     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
+     * References to primitive types are ignored.
+     */
+    public ClassFile[] referencedClassFiles;
+
+
+    protected NameAndTypeCpInfo()
+    {
+    }
+
+
+    /**
+     * Creates a new NameAndTypeCpInfo with the given name and type indices.
+     * @param u2nameIndex          the index of the name in the constant pool.
+     * @param u2descriptorIndex    the index of the descriptor in the constant
+     *                             pool.
+     * @param referencedClassFiles the list of class files referenced in the
+     *                             descriptor string.
+     */
+    public NameAndTypeCpInfo(int         u2nameIndex,
+                             int         u2descriptorIndex,
+                             ClassFile[] referencedClassFiles)
+    {
+        this.u2nameIndex          = u2nameIndex;
+        this.u2descriptorIndex    = u2descriptorIndex;
+        this.referencedClassFiles = referencedClassFiles;
+    }
+
+
+    /**
+     * Returns the name index.
+     */
+    protected int getNameIndex()
+    {
+        return u2nameIndex;
+    }
+
+    /**
+     * Sets the name index.
+     */
+    protected void setNameIndex(int index)
+    {
+        u2nameIndex = index;
+    }
+
+    /**
+     * Returns the descriptor index.
+     */
+    protected int getDescriptorIndex()
+    {
+        return u2descriptorIndex;
+    }
+
+    /**
+     * Sets the descriptor index.
+     */
+    protected void setDescriptorIndex(int index)
+    {
+        u2descriptorIndex = index;
+    }
+
+    /**
+     * Returns the name.
+     */
+    public String getName(ClassFile classFile)
+    {
+        return classFile.getCpString(u2nameIndex);
+    }
+
+    /**
+     * Returns the type.
+     */
+    public String getType(ClassFile classFile)
+    {
+        return classFile.getCpString(u2descriptorIndex);
+    }
+
+
+    // Implementations for CpInfo.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_NameAndType;
+    }
+
+    protected void readInfo(DataInput din) throws IOException
+    {
+        u2nameIndex = din.readUnsignedShort();
+        u2descriptorIndex = din.readUnsignedShort();
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2nameIndex);
+        dout.writeShort(u2descriptorIndex);
+    }
+
+    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
+    {
+        cpInfoVisitor.visitNameAndTypeCpInfo(classFile, this);
+    }
+
+
+    /**
+     * Lets the ClassFile objects referenced in the descriptor string
+     * accept the given visitor.
+     */
+    public void referencedClassesAccept(ClassFileVisitor classFileVisitor)
+    {
+        if (referencedClassFiles != null)
+        {
+            for (int i = 0; i < referencedClassFiles.length; i++)
+            {
+                if (referencedClassFiles[i] != null)
+                {
+                    referencedClassFiles[i].accept(classFileVisitor);
+                }
+            }
+        }
+    }
+}
diff --git a/src/proguard/classfile/ProgramClassFile.java b/src/proguard/classfile/ProgramClassFile.java
new file mode 100644
index 0000000..54d1a19
--- /dev/null
+++ b/src/proguard/classfile/ProgramClassFile.java
@@ -0,0 +1,537 @@
+/* $Id: ProgramClassFile.java,v 1.32 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.*;
+
+import java.io.*;
+
+/**
+ * This is a complete representation of the data in a Java class file.
+ * A ClassFile instance can be generated using the static create(DataInput)
+ * method, manipulated using various operators, and persisted back using the
+ * write(DataOutput) method.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class ProgramClassFile implements ClassFile
+{
+    public int                 u4magic;
+    public int                 u2minorVersion;
+    public int                 u2majorVersion;
+    public int                 u2constantPoolCount;
+    public CpInfo[]            constantPool;
+    public int                 u2accessFlags;
+    public int                 u2thisClass;
+    public int                 u2superClass;
+    public int                 u2interfacesCount;
+    public int[]               u2interfaces;
+    public int                 u2fieldsCount;
+    public ProgramFieldInfo[]  fields;
+    public int                 u2methodsCount;
+    public ProgramMethodInfo[] methods;
+    public int                 u2attributesCount;
+    public AttrInfo[]          attributes;
+
+    /**
+     * An extra field pointing to the subclasses of this class.
+     * This field is filled out by the <code>{@link ClassFileReferenceInitializer}</code>.
+     */
+    public ClassFile[] subClasses = null;
+
+    /**
+     * An extra field in which visitors can store information.
+     */
+    public Object visitorInfo;
+
+
+    /**
+     * Creates a new ClassFile from the class file format data in the DataInput
+     * stream.
+     *
+     * @throws IOException if class file is corrupt or incomplete
+     */
+    public static ClassFile create(DataInput din) throws IOException
+    {
+        ProgramClassFile cf = new ProgramClassFile();
+        cf.read(din);
+        return cf;
+    }
+
+
+    /**
+     * Creates an empty ProgramClassFile.
+     */
+    private ProgramClassFile() {}
+
+
+    /**
+     * Imports the class data into this ProgramClassFile.
+     */
+    private void read(DataInput din) throws IOException
+    {
+        // Read and check the class file magic number.
+        u4magic = din.readInt();
+        ClassUtil.checkMagicNumber(u4magic);
+
+        // Read and check the class file version numbers.
+        u2minorVersion = din.readUnsignedShort();
+        u2majorVersion = din.readUnsignedShort();
+        ClassUtil.checkVersionNumbers(u2majorVersion, u2minorVersion);
+
+        // Read the constant pool.
+        u2constantPoolCount = din.readUnsignedShort();
+        constantPool        = new CpInfo[u2constantPoolCount];
+
+        // Fill the constant pool. The zero entry is not used, nor are the
+        // entries following a Long or Double.
+        for (int i = 1; i < u2constantPoolCount; i++)
+        {
+            constantPool[i] = CpInfo.create(din);
+
+            int tag = constantPool[i].getTag();
+            if (tag == ClassConstants.CONSTANT_Long ||
+                tag == ClassConstants.CONSTANT_Double)
+            {
+                i++;
+            }
+        }
+
+        // Read the access flags, class name, and super class name.
+        u2accessFlags = din.readUnsignedShort();
+        u2thisClass   = din.readUnsignedShort();
+        u2superClass  = din.readUnsignedShort();
+
+        // Read the interfaces.
+        u2interfacesCount = din.readUnsignedShort();
+        u2interfaces      = new int[u2interfacesCount];
+        for (int i = 0; i < u2interfacesCount; i++)
+        {
+            u2interfaces[i] = din.readUnsignedShort();
+        }
+
+        // Read the fields.
+        u2fieldsCount = din.readUnsignedShort();
+        fields        = new ProgramFieldInfo[u2fieldsCount];
+        for (int i = 0; i < u2fieldsCount; i++)
+        {
+            fields[i] = ProgramFieldInfo.create(din, this);
+        }
+
+        // Read the methods.
+        u2methodsCount = din.readUnsignedShort();
+        methods        = new ProgramMethodInfo[u2methodsCount];
+        for (int i = 0; i < u2methodsCount; i++)
+        {
+            methods[i] = ProgramMethodInfo.create(din, this);
+        }
+
+        // Read the attributes.
+        u2attributesCount = din.readUnsignedShort();
+        attributes        = new AttrInfo[u2attributesCount];
+        for (int i = 0; i < u2attributesCount; i++)
+        {
+            attributes[i] = AttrInfo.create(din, this);
+        }
+    }
+
+
+    /**
+     * Exports the representation to a DataOutput stream.
+     */
+    public void write(DataOutput dout) throws IOException
+    {
+        dout.writeInt(u4magic);
+        dout.writeShort(u2minorVersion);
+        dout.writeShort(u2majorVersion);
+        dout.writeShort(u2constantPoolCount);
+        for (int i = 1; i < u2constantPoolCount; i++)
+        {
+            CpInfo cpInfo = constantPool[i];
+            if (cpInfo != null)
+            {
+                cpInfo.write(dout);
+            }
+        }
+        dout.writeShort(u2accessFlags);
+        dout.writeShort(u2thisClass);
+        dout.writeShort(u2superClass);
+        dout.writeShort(u2interfacesCount);
+        for (int i = 0; i < u2interfacesCount; i++)
+        {
+            dout.writeShort(u2interfaces[i]);
+        }
+        dout.writeShort(u2fieldsCount);
+        for (int i = 0; i < u2fieldsCount; i++)
+        {
+            fields[i].write(dout);
+        }
+        dout.writeShort(u2methodsCount);
+        for (int i = 0; i < u2methodsCount; i++)
+        {
+            methods[i].write(dout);
+        }
+        dout.writeShort(u2attributesCount);
+        for (int i = 0; i < u2attributesCount; i++)
+        {
+            attributes[i].write(dout);
+        }
+    }
+
+
+    /**
+     * Returns the CpInfo at the given index in the constant pool.
+     */
+    public CpInfo getCpEntry(int cpIndex)
+    {
+        return constantPool[cpIndex];
+    }
+
+
+    /**
+     * Returns the field with the given name and descriptor.
+     */
+    ProgramFieldInfo findProgramField(String name, String descriptor)
+    {
+        for (int i = 0; i < u2fieldsCount; i++)
+        {
+            ProgramFieldInfo field = fields[i];
+            if ((name       == null || field.getName(this).equals(name)) &&
+                (descriptor == null || field.getDescriptor(this).equals(descriptor)))
+            {
+                return field;
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Returns the method with the given name and descriptor.
+     */
+    ProgramMethodInfo findProgramMethod(String name, String descriptor)
+    {
+        for (int i = 0; i < u2methodsCount; i++)
+        {
+            ProgramMethodInfo method = methods[i];
+            if ((name       == null || method.getName(this).equals(name)) &&
+                (descriptor == null || method.getDescriptor(this).equals(descriptor)))
+            {
+                return method;
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Returns the attribute specified by the given name.
+     */
+    AttrInfo getAttribute(String name)
+    {
+        for (int i = 0; i < u2attributesCount; i++)
+        {
+            AttrInfo attribute = attributes[i];
+            if (attribute.getAttributeName(this).equals(name))
+            {
+                return attribute;
+            }
+        }
+
+        return null;
+    }
+
+
+    // Implementations for ClassFile.
+
+    public int getAccessFlags()
+    {
+        return u2accessFlags;
+    }
+
+    public String getName()
+    {
+        return getCpClassNameString(u2thisClass);
+    }
+
+    public String getSuperName()
+    {
+        return u2superClass == 0 ? null : getCpClassNameString(u2superClass);
+    }
+
+    public String getInterfaceName(int index)
+    {
+        return getCpClassNameString(u2interfaces[index]);
+    }
+
+    public int getCpTag(int cpIndex)
+    {
+        return constantPool[cpIndex].getTag();
+    }
+
+    public String getCpString(int cpIndex)
+    {
+        return ((Utf8CpInfo)constantPool[cpIndex]).getString();
+    }
+
+    public String getCpClassNameString(int cpIndex)
+    {
+        ClassCpInfo classEntry = (ClassCpInfo)constantPool[cpIndex];
+        Utf8CpInfo  nameEntry  = (Utf8CpInfo)constantPool[classEntry.getNameIndex()];
+
+        return nameEntry.getString();
+    }
+
+    public String getCpNameString(int cpIndex)
+    {
+        return ((NameAndTypeCpInfo)constantPool[cpIndex]).getName(this);
+    }
+
+    public String getCpTypeString(int cpIndex)
+    {
+        return ((NameAndTypeCpInfo)constantPool[cpIndex]).getType(this);
+    }
+
+
+    public void addSubClass(ClassFile classFile)
+    {
+        if (subClasses == null)
+        {
+            subClasses = new ClassFile[1];
+        }
+        else
+        {
+            // Copy the old elements into new larger array.
+            ClassFile[] temp = new ClassFile[subClasses.length+1];
+            System.arraycopy(subClasses, 0, temp, 0, subClasses.length);
+            subClasses = temp;
+        }
+
+        subClasses[subClasses.length-1] = classFile;
+    }
+
+
+    public ClassFile getSuperClass()
+    {
+        return u2superClass != 0 ?
+            ((ClassCpInfo)constantPool[u2superClass]).referencedClassFile :
+            null;
+    }
+
+
+    public ClassFile getInterface(int index)
+    {
+        return ((ClassCpInfo)constantPool[u2interfaces[index]]).referencedClassFile;
+    }
+
+
+    public boolean extends_(ClassFile classFile)
+    {
+        if (this.equals(classFile))
+        {
+            return true;
+        }
+
+        ClassFile superClass = getSuperClass();
+        return superClass != null &&
+               superClass.extends_(classFile);
+    }
+
+
+    public boolean implements_(ClassFile classFile)
+    {
+        if (this.equals(classFile))
+        {
+            return true;
+        }
+
+        for (int i = 0; i < u2interfacesCount; i++)
+        {
+            ClassFile interfaceClass = getInterface(i);
+            if (interfaceClass != null &&
+                interfaceClass.implements_(classFile))
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+    public FieldInfo findField(String name, String descriptor)
+    {
+        return findProgramField(name, descriptor);
+    }
+
+
+    public MethodInfo findMethod(String name, String descriptor)
+    {
+        return findProgramMethod(name, descriptor);
+    }
+
+
+    public void accept(ClassFileVisitor classFileVisitor)
+    {
+        classFileVisitor.visitProgramClassFile(this);
+    }
+
+
+    public void hierarchyAccept(boolean          visitThisClass,
+                                boolean          visitSuperClass,
+                                boolean          visitInterfaces,
+                                boolean          visitSubclasses,
+                                ClassFileVisitor classFileVisitor)
+    {
+        // First visit the current classfile.
+        if (visitThisClass)
+        {
+            accept(classFileVisitor);
+        }
+
+        // Then visit its superclass, recursively.
+        if (visitSuperClass)
+        {
+            ClassFile superClass = getSuperClass();
+            if (superClass != null)
+            {
+                superClass.hierarchyAccept(true,
+                                           true,
+                                           visitInterfaces,
+                                           false,
+                                           classFileVisitor);
+            }
+        }
+
+        // Then visit its interfaces, recursively.
+        if (visitInterfaces)
+        {
+            for (int i = 0; i < u2interfacesCount; i++)
+            {
+                ClassFile interfaceClass = getInterface(i);
+                if (interfaceClass != null)
+                {
+                    interfaceClass.hierarchyAccept(true,
+                                                   true,
+                                                   true,
+                                                   false,
+                                                   classFileVisitor);
+                }
+            }
+        }
+
+        // Then visit its subclasses, recursively.
+        if (visitSubclasses)
+        {
+            if (subClasses != null)
+            {
+                for (int i = 0; i < subClasses.length; i++)
+                {
+                    ClassFile subClass = subClasses[i];
+                    subClass.hierarchyAccept(true,
+                                             false,
+                                             false,
+                                             true,
+                                             classFileVisitor);
+                }
+            }
+        }
+    }
+
+
+    public void constantPoolEntriesAccept(CpInfoVisitor cpInfoVisitor)
+    {
+        for (int i = 1; i < u2constantPoolCount; i++)
+        {
+            if (constantPool[i] != null)
+            {
+                constantPool[i].accept(this, cpInfoVisitor);
+            }
+        }
+    }
+
+    public void constantPoolEntryAccept(int index, CpInfoVisitor cpInfoVisitor)
+    {
+        constantPool[index].accept(this, cpInfoVisitor);
+    }
+
+    public void fieldsAccept(MemberInfoVisitor memberInfoVisitor)
+    {
+        for (int i = 0; i < u2fieldsCount; i++)
+        {
+            fields[i].accept(this, memberInfoVisitor);
+        }
+    }
+
+    public void fieldAccept(String name, String descriptor, MemberInfoVisitor memberInfoVisitor)
+    {
+        ProgramFieldInfo field = findProgramField(name, descriptor);
+        if (field != null)
+        {
+            field.accept(this, memberInfoVisitor);
+        }
+    }
+
+    public void methodsAccept(MemberInfoVisitor memberInfoVisitor)
+    {
+        for (int i = 0; i < u2methodsCount; i++)
+        {
+            methods[i].accept(this, memberInfoVisitor);
+        }
+    }
+
+    public void methodAccept(String name, String descriptor, MemberInfoVisitor memberInfoVisitor)
+    {
+        ProgramMethodInfo method = findProgramMethod(name, descriptor);
+        if (method != null)
+        {
+            method.accept(this, memberInfoVisitor);
+        }
+    }
+
+    public void attributesAccept(AttrInfoVisitor attrInfoVisitor)
+    {
+        for (int i = 0; i < u2attributesCount; i++)
+        {
+            attributes[i].accept(this, attrInfoVisitor);
+        }
+    }
+
+
+    // Implementations for VisitorAccepter.
+
+    public Object getVisitorInfo()
+    {
+        return visitorInfo;
+    }
+
+    public void setVisitorInfo(Object visitorInfo)
+    {
+        this.visitorInfo = visitorInfo;
+    }
+}
diff --git a/src/proguard/classfile/ProgramFieldInfo.java b/src/proguard/classfile/ProgramFieldInfo.java
new file mode 100644
index 0000000..f36993f
--- /dev/null
+++ b/src/proguard/classfile/ProgramFieldInfo.java
@@ -0,0 +1,70 @@
+/* $Id: ProgramFieldInfo.java,v 1.16 2004/10/23 16:53:01 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.*;
+
+import java.io.*;
+
+/**
+ * Representation of a field from a program class file.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class ProgramFieldInfo extends ProgramMemberInfo implements FieldInfo
+{
+    /**
+     * Creates a new ProgramFieldInfo from the file format data in the DataInput stream.
+     *
+     * @throws IOException if class file is corrupt or incomplete
+     */
+    public static ProgramFieldInfo create(DataInput din, ClassFile cf) throws IOException
+    {
+        ProgramFieldInfo fi = new ProgramFieldInfo();
+        fi.read(din, cf);
+        return fi;
+    }
+
+
+    protected ProgramFieldInfo()
+    {
+    }
+
+
+    // Implementations for ProgramMemberInfo.
+
+    public void accept(ProgramClassFile programClassFile, MemberInfoVisitor memberInfoVisitor)
+    {
+        memberInfoVisitor.visitProgramFieldInfo(programClassFile, this);
+    }
+
+
+    public void attributesAccept(ProgramClassFile programClassFile, AttrInfoVisitor attrInfoVisitor)
+    {
+        for (int i = 0; i < u2attributesCount; i++)
+        {
+            attributes[i].accept(programClassFile, this, attrInfoVisitor);
+        }
+    }
+}
diff --git a/src/proguard/classfile/ProgramMemberInfo.java b/src/proguard/classfile/ProgramMemberInfo.java
new file mode 100644
index 0000000..7b3bade
--- /dev/null
+++ b/src/proguard/classfile/ProgramMemberInfo.java
@@ -0,0 +1,208 @@
+/* $Id: ProgramMemberInfo.java,v 1.27 2004/11/14 00:54:38 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+
+import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.*;
+
+import java.io.*;
+
+/**
+ * Representation of a field or method from a program class file.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+abstract public class ProgramMemberInfo implements MemberInfo
+{
+    public int        u2accessFlags;
+    public int        u2nameIndex;
+    public int        u2descriptorIndex;
+    public int        u2attributesCount;
+    public AttrInfo[] attributes;
+
+    /**
+     * An extra field pointing to the ClassFile objects referenced in the
+     * descriptor string. This field is filled out by the <code>{@link
+     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
+     * References to primitive types are ignored.
+     */
+    public ClassFile[] referencedClassFiles;
+
+    /**
+     * An extra field in which visitors can store information.
+     */
+    public Object visitorInfo;
+
+
+    protected ProgramMemberInfo() {}
+
+
+    /**
+     * Returns the line number range of the given class member as "m:n",
+     * if it can find it, or <code>null</code> otherwise.
+     */
+    public String getLineNumberRange(ClassFile classFile)
+    {
+        CodeAttrInfo codeAttribute =
+            (CodeAttrInfo)getAttribute(classFile, ClassConstants.ATTR_Code);
+        if (codeAttribute == null)
+        {
+            return null;
+        }
+
+        LineNumberTableAttrInfo lineNumberTableAttribute =
+            (LineNumberTableAttrInfo)codeAttribute.getAttribute(classFile,
+                                                                ClassConstants.ATTR_LineNumberTable);
+        if (lineNumberTableAttribute == null)
+        {
+            return null;
+        }
+
+        return "" +
+               lineNumberTableAttribute.getLineNumber(0) +
+               ":" +
+               lineNumberTableAttribute.getLineNumber(Integer.MAX_VALUE);
+    }
+
+
+    /**
+     * Returns the (first) attribute with the given name.
+     */
+    private AttrInfo getAttribute(ClassFile classFile, String name)
+    {
+        for (int i = 0; i < u2attributesCount; i++)
+        {
+            AttrInfo attribute = attributes[i];
+            if (attribute.getAttributeName(classFile).equals(name))
+            {
+                return attribute;
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Accepts the given member info visitor.
+     */
+    public abstract void accept(ProgramClassFile  programClassFile,
+                                MemberInfoVisitor memberInfoVisitor);
+
+
+
+    /**
+     * Lets the given attribute info visitor visit all the attributes of
+     * this member info.
+     */
+    public abstract void attributesAccept(ProgramClassFile programClassFile,
+                                          AttrInfoVisitor  attrInfoVisitor);
+
+
+    /**
+     * Lets the ClassFile objects referenced in the descriptor string
+     * accept the given visitor.
+     */
+    public void referencedClassesAccept(ClassFileVisitor classFileVisitor)
+    {
+        if (referencedClassFiles != null)
+        {
+            for (int i = 0; i < referencedClassFiles.length; i++)
+            {
+                if (referencedClassFiles[i] != null)
+                {
+                    referencedClassFiles[i].accept(classFileVisitor);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Imports the field or method data to internal representation.
+     */
+    protected void read(DataInput din, ClassFile cf) throws IOException
+    {
+        u2accessFlags = din.readUnsignedShort();
+        u2nameIndex = din.readUnsignedShort();
+        u2descriptorIndex = din.readUnsignedShort();
+        u2attributesCount = din.readUnsignedShort();
+        attributes = new AttrInfo[u2attributesCount];
+        for (int i = 0; i < u2attributesCount; i++)
+        {
+            attributes[i] = AttrInfo.create(din, cf);
+        }
+    }
+
+    /**
+     * Exports the representation to a DataOutput stream.
+     */
+    public void write(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2accessFlags);
+        dout.writeShort(u2nameIndex);
+        dout.writeShort(u2descriptorIndex);
+        dout.writeShort(u2attributesCount);
+        for (int i = 0; i < u2attributesCount; i++)
+        {
+            attributes[i].write(dout);
+        }
+    }
+
+
+    // Implementations for MemberInfo.
+
+    public int getAccessFlags()
+    {
+        return u2accessFlags;
+    }
+
+    public String getName(ClassFile classFile)
+    {
+        return classFile.getCpString(u2nameIndex);
+    }
+
+    public String getDescriptor(ClassFile classFile)
+    {
+        return classFile.getCpString(u2descriptorIndex);
+    }
+
+    public void accept(ClassFile classFile, MemberInfoVisitor memberInfoVisitor)
+    {
+        accept((ProgramClassFile)classFile, memberInfoVisitor);
+    }
+
+
+    // Implementations for VisitorAccepter.
+
+    public Object getVisitorInfo()
+    {
+        return visitorInfo;
+    }
+
+    public void setVisitorInfo(Object visitorInfo)
+    {
+        this.visitorInfo = visitorInfo;
+    }
+}
diff --git a/src/proguard/classfile/ProgramMethodInfo.java b/src/proguard/classfile/ProgramMethodInfo.java
new file mode 100644
index 0000000..b46a733
--- /dev/null
+++ b/src/proguard/classfile/ProgramMethodInfo.java
@@ -0,0 +1,70 @@
+/* $Id: ProgramMethodInfo.java,v 1.16 2004/10/23 16:53:01 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.*;
+
+import java.io.*;
+
+/**
+ * Representation of a method from a program class file.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class ProgramMethodInfo extends ProgramMemberInfo implements MethodInfo
+{
+    /**
+     * Creates a new ProgramMethodInfo from the file format data in the DataInput stream.
+     *
+     * @throws IOException if class file is corrupt or incomplete
+     */
+    public static ProgramMethodInfo create(DataInput din, ClassFile cf) throws IOException
+    {
+        ProgramMethodInfo mi = new ProgramMethodInfo();
+        mi.read(din, cf);
+        return mi;
+    }
+
+
+    public ProgramMethodInfo()
+    {
+    }
+
+
+    // Implementations for ProgramMemberInfo.
+
+    public void accept(ProgramClassFile programClassFile, MemberInfoVisitor memberInfoVisitor)
+    {
+        memberInfoVisitor.visitProgramMethodInfo(programClassFile, this);
+    }
+
+
+    public void attributesAccept(ProgramClassFile programClassFile, AttrInfoVisitor attrInfoVisitor)
+    {
+        for (int i = 0; i < u2attributesCount; i++)
+        {
+            attributes[i].accept(programClassFile, this, attrInfoVisitor);
+        }
+    }
+}
diff --git a/src/proguard/classfile/RefCpInfo.java b/src/proguard/classfile/RefCpInfo.java
new file mode 100644
index 0000000..37b2637
--- /dev/null
+++ b/src/proguard/classfile/RefCpInfo.java
@@ -0,0 +1,146 @@
+/* $Id: RefCpInfo.java,v 1.21 2004/10/31 18:13:37 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+
+/**
+ * Representation of a 'ref'-type entry in the ConstantPool.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public abstract class RefCpInfo extends CpInfo
+{
+    public int u2classIndex;
+    public int u2nameAndTypeIndex;
+
+    /**
+     * An extra field pointing to the referenced ClassFile object.
+     * This field is typically filled out by the <code>{@link
+     * ClassFileReferenceInitializer}</code>.
+     */
+    public ClassFile referencedClassFile;
+
+    /**
+     * An extra field optionally pointing to the referenced MemberInfo object.
+     * This field is typically filled out by the <code>{@link
+     * ClassFileReferenceInitializer}</code>.
+     */
+    public MemberInfo referencedMemberInfo;
+
+
+    protected RefCpInfo()
+    {
+    }
+
+
+    /**
+     * Returns the class index.
+     */
+    public int getClassIndex()
+    {
+        return u2classIndex;
+    }
+
+    /**
+     * Returns the name-and-type index.
+     */
+    public int getNameAndTypeIndex()
+    {
+        return u2nameAndTypeIndex;
+    }
+
+    /**
+     * Sets the name-and-type index.
+     */
+    public void setNameAndTypeIndex(int index)
+    {
+        u2nameAndTypeIndex = index;
+    }
+
+    /**
+     * Returns the class name.
+     */
+    public String getClassName(ClassFile classFile)
+    {
+        return classFile.getCpClassNameString(u2classIndex);
+    }
+
+    /**
+     * Returns the method/field name.
+     */
+    public String getName(ClassFile classFile)
+    {
+        return classFile.getCpNameString(u2nameAndTypeIndex);
+    }
+
+    /**
+     * Returns the type.
+     */
+    public String getType(ClassFile classFile)
+    {
+        return classFile.getCpTypeString(u2nameAndTypeIndex);
+    }
+
+
+    /**
+     * Lets the referenced class file accept the given visitor.
+     */
+    public void referencedClassAccept(ClassFileVisitor classFileVisitor)
+    {
+        if (referencedClassFile != null)
+        {
+            referencedClassFile.accept(classFileVisitor);
+        }
+    }
+
+
+    /**
+     * Lets the referenced class member accept the given visitor.
+     */
+    public void referencedMemberInfoAccept(MemberInfoVisitor memberInfoVisitor)
+    {
+        if (referencedMemberInfo != null)
+        {
+            referencedMemberInfo.accept(referencedClassFile,
+                                        memberInfoVisitor);
+        }
+    }
+
+
+    // Implementations for CpInfo.
+
+    protected void readInfo(DataInput din) throws IOException
+    {
+        u2classIndex = din.readUnsignedShort();
+        u2nameAndTypeIndex = din.readUnsignedShort();
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2classIndex);
+        dout.writeShort(u2nameAndTypeIndex);
+    }
+}
diff --git a/src/proguard/classfile/StringCpInfo.java b/src/proguard/classfile/StringCpInfo.java
new file mode 100644
index 0000000..9fd8021
--- /dev/null
+++ b/src/proguard/classfile/StringCpInfo.java
@@ -0,0 +1,108 @@
+/* $Id: StringCpInfo.java,v 1.17 2004/11/07 18:49:09 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+
+/**
+ * Representation of a 'string' entry in the ConstantPool.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class StringCpInfo extends CpInfo
+{
+    public int u2stringIndex;
+
+    /**
+     * An extra field pointing to the referenced ClassFile object, if this
+     * string is being used in Class.forName() or .class constructs.
+     * This field is filled out by the <code>{@link
+     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
+     * References to library class files are not filled out.
+     */
+    public ClassFile referencedClassFile;
+
+
+    protected StringCpInfo()
+    {
+    }
+
+
+    /**
+     * Creates a new StringCpInfo with the given string index.
+     * @param u2nameIndex         the index of the string in the constant pool.
+     * @param referencedClassFile the referenced class file, if any.
+     */
+    public StringCpInfo(int       u2stringIndex,
+                        ClassFile referencedClassFile)
+    {
+        this.u2stringIndex       = u2stringIndex;
+        this.referencedClassFile = referencedClassFile;
+    }
+
+
+    /**
+     * Returns the string value.
+     */
+    public String getString(ClassFile classFile)
+    {
+        return classFile.getCpString(u2stringIndex);
+    }
+
+
+    // Implementations for CpInfo.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_String;
+    }
+
+    protected void readInfo(DataInput din) throws IOException
+    {
+        u2stringIndex = din.readUnsignedShort();
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2stringIndex);
+    }
+
+    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
+    {
+        cpInfoVisitor.visitStringCpInfo(classFile, this);
+    }
+
+
+    /**
+     * Lets the referenced class file accept the given visitor.
+     */
+    public void referencedClassAccept(ClassFileVisitor classFileVisitor)
+    {
+        if (referencedClassFile != null)
+        {
+            referencedClassFile.accept(classFileVisitor);
+        }
+    }
+}
diff --git a/src/proguard/classfile/Utf8CpInfo.java b/src/proguard/classfile/Utf8CpInfo.java
new file mode 100644
index 0000000..35faa3d
--- /dev/null
+++ b/src/proguard/classfile/Utf8CpInfo.java
@@ -0,0 +1,215 @@
+/* $Id: Utf8CpInfo.java,v 1.20 2004/10/23 16:53:01 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+
+/**
+ * Representation of a 'UTF-8' entry in the ConstantPool.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class Utf8CpInfo extends CpInfo
+{
+    private static final String ENCODING = "UTF-8";
+
+    private static final char TWO_BYTE_LIMIT     = 0x80;
+    private static final byte TWO_BYTE_CONSTANT1 = (byte)0xc0;
+    private static final byte TWO_BYTE_CONSTANT2 = (byte)0x80;
+    private static final int  TWO_BYTE_SHIFT1    = 6;
+    private static final byte TWO_BYTE_MASK1     = (byte)0x1f;
+    private static final byte TWO_BYTE_MASK2     = (byte)0x3f;
+
+    private static final char THREE_BYTE_LIMIT     = 0x800;
+    private static final byte THREE_BYTE_CONSTANT1 = (byte)0xe0;
+    private static final byte THREE_BYTE_CONSTANT2 = (byte)0x80;
+    private static final byte THREE_BYTE_CONSTANT3 = (byte)0x80;
+    private static final int  THREE_BYTE_SHIFT1    = 12;
+    private static final int  THREE_BYTE_SHIFT2    = 6;
+    private static final byte THREE_BYTE_MASK1     = (byte)0x0f;
+    private static final byte THREE_BYTE_MASK2     = (byte)0x3f;
+    private static final byte THREE_BYTE_MASK3     = (byte)0x3f;
+
+
+    // There are a lot of Utf8CpInfo objects, so we're optimising their storage.
+    // Initially, we're storing the UTF-8 bytes in a byte array.
+    // When the corresponding String is requested, we ditch the array and just
+    // store the String.
+
+    //private int u2length;
+    private byte[] bytes;
+
+    private String utf8string;
+
+
+    protected Utf8CpInfo()
+    {
+    }
+
+    /**
+     * Constructor used when appending fresh UTF-8 entries to the constant pool.
+     */
+    public Utf8CpInfo(String utf8string)
+    {
+        this.bytes      = null;
+        this.utf8string = utf8string;
+    }
+
+    /**
+     * Returns UTF-8 data as a String.
+     */
+    public String getString()
+    {
+        try
+        {
+            switchToStringRepresentation();
+        }
+        catch (UnsupportedEncodingException ex)
+        {
+        }
+
+        return utf8string;
+    }
+
+    /**
+     * Sets UTF-8 data as String.
+     */
+    public void setString(String utf8String) throws Exception
+    {
+        this.bytes      = null;
+        this.utf8string = utf8String;
+    }
+
+
+    // Implementations for CpInfo.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_Utf8;
+    }
+
+    protected void readInfo(DataInput din) throws IOException
+    {
+        int u2length = din.readUnsignedShort();
+        bytes = new byte[u2length];
+        din.readFully(bytes);
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        byte[] bytes    = getByteArrayRepresentation();
+        int    u2length = bytes.length;
+
+        dout.writeShort(u2length);
+        dout.write(bytes);
+    }
+
+    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
+    {
+        cpInfoVisitor.visitUtf8CpInfo(classFile, this);
+    }
+
+
+    /**
+     * Switches to a String representation of the UTF-8 data.
+     */
+    private void switchToStringRepresentation() throws UnsupportedEncodingException
+    {
+        if (utf8string == null)
+        {
+            utf8string = new String(bytes, ENCODING);
+            bytes = null;
+        }
+    }
+
+
+    /**
+     * Transforms UTF-8 bytes to the slightly modified UTF-8 representation that
+     * is used by class files.
+     */
+    private byte[] getByteArrayRepresentation() throws UnsupportedEncodingException
+    {
+        // Do we still have the byte array representation?
+        if (bytes != null)
+        {
+            // Then return that one.
+            return bytes;
+        }
+
+        // We're computing the byte array ourselves, because the implementation
+        // of String.getBytes("UTF-8") has a bug, at least up to JRE 1.4.2.
+        // Also note the special treatment of the 0 character.
+
+        // Compute the byte array length.
+        int byteLength   = 0;
+        int stringLength = utf8string.length();
+        for (int stringIndex = 0; stringIndex < stringLength; stringIndex++)
+        {
+            char c = utf8string.charAt(stringIndex);
+
+            // The character is represented by one, two, or three bytes.
+            byteLength += c == 0                ? 2 :
+                          c <  TWO_BYTE_LIMIT   ? 1 :
+                          c <  THREE_BYTE_LIMIT ? 2 :
+                                                  3;
+        }
+
+        // Allocate the byte array with the computed length.
+        byte[] bytes  = new byte[byteLength];
+
+        // Fill out the array.
+        int byteIndex = 0;
+        for (int stringIndex = 0; stringIndex < stringLength; stringIndex++)
+        {
+            char c = utf8string.charAt(stringIndex);
+            if (c == 0)
+            {
+                // The 0 character gets a two-byte representation in class files.
+                bytes[byteIndex++] = TWO_BYTE_CONSTANT1;
+                bytes[byteIndex++] = TWO_BYTE_CONSTANT2;
+            }
+            else if (c < TWO_BYTE_LIMIT)
+            {
+                // The character is represented by a single byte.
+                bytes[byteIndex++] = (byte)c;
+            }
+            else if (c < THREE_BYTE_LIMIT)
+            {
+                // The character is represented by two bytes.
+                bytes[byteIndex++] = (byte)(TWO_BYTE_CONSTANT1 | ((c >>> TWO_BYTE_SHIFT1) & TWO_BYTE_MASK1));
+                bytes[byteIndex++] = (byte)(TWO_BYTE_CONSTANT2 | ( c                      & TWO_BYTE_MASK2));
+            }
+            else
+            {
+                // The character is represented by three bytes.
+                bytes[byteIndex++] = (byte)(THREE_BYTE_CONSTANT1 | ((c >>> THREE_BYTE_SHIFT1) & THREE_BYTE_MASK1));
+                bytes[byteIndex++] = (byte)(THREE_BYTE_CONSTANT2 | ((c >>> THREE_BYTE_SHIFT2) & THREE_BYTE_MASK2));
+                bytes[byteIndex++] = (byte)(THREE_BYTE_CONSTANT3 | ( c                        & THREE_BYTE_MASK3));
+            }
+        }
+
+        return bytes;
+    }
+}
diff --git a/src/proguard/classfile/VisitorAccepter.java b/src/proguard/classfile/VisitorAccepter.java
new file mode 100644
index 0000000..d5d8b31
--- /dev/null
+++ b/src/proguard/classfile/VisitorAccepter.java
@@ -0,0 +1,48 @@
+/* $Id: VisitorAccepter.java,v 1.11 2004/10/23 16:53:01 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+
+
+
+/**
+ * This interface is a base interface for visitor accepters. It allows
+ * visitors to set and get any temporary information they desire on the
+ * objects they are visiting. Note that every visitor accepter has only one
+ * such property, so visitors will have to take care not to overwrite each
+ * other's information, if it is still required.
+ *
+ * @author Eric Lafortune
+ */
+public interface VisitorAccepter
+{
+    /**
+     * Gets the visitor information of the visitor accepter.
+     */
+    public Object getVisitorInfo();
+
+
+    /**
+     * Sets the visitor information of the visitor accepter.
+     */
+    public void setVisitorInfo(Object visitorInfo);
+}
diff --git a/src/proguard/classfile/attribute/AllAttrInfoVisitor.java b/src/proguard/classfile/attribute/AllAttrInfoVisitor.java
new file mode 100644
index 0000000..ca8582e
--- /dev/null
+++ b/src/proguard/classfile/attribute/AllAttrInfoVisitor.java
@@ -0,0 +1,68 @@
+/* $Id: AllAttrInfoVisitor.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This MemberInfoVisitor lets a given AttrInfoVisitor visit all AttrInfo
+ * objects of the program class members it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class AllAttrInfoVisitor implements MemberInfoVisitor
+{
+    private AttrInfoVisitor attrInfoVisitor;
+
+
+    public AllAttrInfoVisitor(AttrInfoVisitor attrInfoVisitor)
+    {
+        this.attrInfoVisitor = attrInfoVisitor;
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        programFieldInfo.attributesAccept(programClassFile, attrInfoVisitor);
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        programMethodInfo.attributesAccept(programClassFile, attrInfoVisitor);
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    {
+        // Library class file fields don't have attributes.
+    }
+
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        // Library class file methods don't have attributes.
+    }
+}
diff --git a/src/proguard/classfile/attribute/AttrInfo.java b/src/proguard/classfile/attribute/AttrInfo.java
new file mode 100644
index 0000000..c7a0b31
--- /dev/null
+++ b/src/proguard/classfile/attribute/AttrInfo.java
@@ -0,0 +1,184 @@
+/* $Id: AttrInfo.java,v 1.3 2004/11/26 17:19:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.annotation.*;
+
+import java.io.*;
+
+/**
+ * Representation of an attribute. Specific attributes have their representations
+ * sub-classed from this.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public abstract class AttrInfo implements VisitorAccepter
+{
+    protected static final int CONSTANT_FIELD_SIZE = 6;
+
+
+    public int u2attrNameIndex;
+    //public int  u4attrLength;
+    //public byte info[];
+
+    /**
+     * An extra field in which visitors can store information.
+     */
+    public Object visitorInfo;
+
+
+    /**
+     * Creates a new AttrInfo from the data passed.
+     *
+     * @throws java.io.IOException if class file is corrupt or incomplete
+     */
+    public static AttrInfo create(DataInput din, ClassFile classFile) throws IOException
+    {
+        // Instantiate based on attribute name
+        int u2attrNameIndex = din.readUnsignedShort();
+        int u4attrLength    = din.readInt();
+        String attrName = classFile.getCpString(u2attrNameIndex);
+        AttrInfo attrInfo =
+            attrName.equals(ClassConstants.ATTR_InnerClasses)                         ? (AttrInfo)new InnerClassesAttrInfo():
+            attrName.equals(ClassConstants.ATTR_EnclosingMethod)                      ? (AttrInfo)new EnclosingMethodAttrInfo():
+            attrName.equals(ClassConstants.ATTR_ConstantValue)                        ? (AttrInfo)new ConstantValueAttrInfo():
+            attrName.equals(ClassConstants.ATTR_Exceptions)                           ? (AttrInfo)new ExceptionsAttrInfo():
+            attrName.equals(ClassConstants.ATTR_Code)                                 ? (AttrInfo)new CodeAttrInfo():
+            attrName.equals(ClassConstants.ATTR_LineNumberTable)                      ? (AttrInfo)new LineNumberTableAttrInfo():
+            attrName.equals(ClassConstants.ATTR_LocalVariableTable)                   ? (AttrInfo)new LocalVariableTableAttrInfo():
+            attrName.equals(ClassConstants.ATTR_LocalVariableTypeTable)               ? (AttrInfo)new LocalVariableTypeTableAttrInfo():
+            attrName.equals(ClassConstants.ATTR_SourceFile)                           ? (AttrInfo)new SourceFileAttrInfo():
+            attrName.equals(ClassConstants.ATTR_SourceDir)                            ? (AttrInfo)new SourceDirAttrInfo():
+            attrName.equals(ClassConstants.ATTR_Deprecated)                           ? (AttrInfo)new DeprecatedAttrInfo():
+            attrName.equals(ClassConstants.ATTR_Synthetic)                            ? (AttrInfo)new SyntheticAttrInfo():
+            attrName.equals(ClassConstants.ATTR_Signature)                            ? (AttrInfo)new SignatureAttrInfo():
+            attrName.equals(ClassConstants.ATTR_RuntimeVisibleAnnotations)            ? (AttrInfo)new RuntimeVisibleAnnotationsAttrInfo():
+            attrName.equals(ClassConstants.ATTR_RuntimeInvisibleAnnotations)          ? (AttrInfo)new RuntimeInvisibleAnnotationsAttrInfo():
+            attrName.equals(ClassConstants.ATTR_RuntimeVisibleParameterAnnotations)   ? (AttrInfo)new RuntimeVisibleParameterAnnotationsAttrInfo():
+            attrName.equals(ClassConstants.ATTR_RuntimeInvisibleParameterAnnotations) ? (AttrInfo)new RuntimeInvisibleParameterAnnotationsAttrInfo():
+            attrName.equals(ClassConstants.ATTR_AnnotationDefault)                    ? (AttrInfo)new AnnotationDefaultAttrInfo():
+                                                                                        (AttrInfo)new UnknownAttrInfo(u4attrLength);
+        attrInfo.u2attrNameIndex = u2attrNameIndex;
+        attrInfo.readInfo(din, classFile);
+
+        return attrInfo;
+    }
+
+
+    protected AttrInfo()
+    {
+    }
+
+
+    /**
+     * Exports the representation to a DataOutput stream.
+     */
+    public final void write(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2attrNameIndex);
+        dout.writeInt(getLength());
+        writeInfo(dout);
+    }
+
+
+    /**
+     * Returns the String name of the attribute; override this in subclasses.
+     */
+    public String getAttributeName(ClassFile classFile)
+    {
+        return classFile.getCpString(u2attrNameIndex);
+    }
+
+
+    // Abstract methods to be implemented by extensions.
+
+    /**
+     * Returns the length of the attribute, expressed in bytes.
+     */
+    protected abstract int getLength();
+
+
+    /**
+     * Reads the data following the header.
+     */
+    protected abstract void readInfo(DataInput din, ClassFile classFile) throws IOException;
+
+
+    /**
+     * Exports data following the header to a DataOutput stream.
+     */
+    protected abstract void writeInfo(DataOutput dout) throws IOException;
+
+
+    /**
+     * Accepts the given visitor.
+     * This default implementation does nothing, which is useful for attributes
+     * that require a context.
+     */
+    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
+    {
+        // Do nothing.
+    }
+
+    /**
+     * Accepts the given visitor in the context of a field.
+     * This default implementation ignores the context.
+     */
+    public void accept(ClassFile classFile, FieldInfo fieldInfo, AttrInfoVisitor attrInfoVisitor)
+    {
+        accept(classFile, attrInfoVisitor);
+    }
+
+    /**
+     * Accepts the given visitor in the context of a method.
+     * This default implementation ignores the context.
+     */
+    public void accept(ClassFile classFile, MethodInfo methodInfo, AttrInfoVisitor attrInfoVisitor)
+    {
+        accept(classFile, attrInfoVisitor);
+    }
+
+    /**
+     * Accepts the given visitor in the context of a method's code.
+     * This default implementation ignores the code.
+     */
+    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, AttrInfoVisitor attrInfoVisitor)
+    {
+        accept(classFile, methodInfo, attrInfoVisitor);
+    }
+
+
+    // Implementations for VisitorAccepter.
+
+    public Object getVisitorInfo()
+    {
+        return visitorInfo;
+    }
+
+    public void setVisitorInfo(Object visitorInfo)
+    {
+        this.visitorInfo = visitorInfo;
+    }
+}
diff --git a/src/proguard/classfile/attribute/AttrInfoVisitor.java b/src/proguard/classfile/attribute/AttrInfoVisitor.java
new file mode 100644
index 0000000..3388d37
--- /dev/null
+++ b/src/proguard/classfile/attribute/AttrInfoVisitor.java
@@ -0,0 +1,59 @@
+/* $Id: AttrInfoVisitor.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+
+/**
+ * This interface specifies the methods for a visitor of <code>AttrInfo</code>
+ * objects.
+ *
+ * @author Eric Lafortune
+ */
+public interface AttrInfoVisitor
+{
+    public void visitUnknownAttrInfo(               ClassFile classFile, UnknownAttrInfo         unknownAttrInfo);
+    public void visitInnerClassesAttrInfo(          ClassFile classFile, InnerClassesAttrInfo    innerClassesAttrInfo);
+    public void visitEnclosingMethodAttrInfo(       ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo);
+
+    public void visitConstantValueAttrInfo(         ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo);
+
+    public void visitExceptionsAttrInfo(            ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo);
+    public void visitCodeAttrInfo(                  ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo       codeAttrInfo);
+
+    public void visitLineNumberTableAttrInfo(       ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo        lineNumberTableAttrInfo);
+    public void visitLocalVariableTableAttrInfo(    ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo     localVariableTableAttrInfo);
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo);
+
+    public void visitSourceFileAttrInfo(            ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo);
+    public void visitSourceDirAttrInfo(             ClassFile classFile, SourceDirAttrInfo  sourceDirAttrInfo);
+    public void visitDeprecatedAttrInfo(            ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo);
+    public void visitSyntheticAttrInfo(             ClassFile classFile, SyntheticAttrInfo  syntheticAttrInfo);
+    public void visitSignatureAttrInfo(             ClassFile classFile, SignatureAttrInfo  syntheticAttrInfo);
+
+    public void visitRuntimeVisibleAnnotationAttrInfo(           ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo            runtimeVisibleAnnotationsAttrInfo);
+    public void visitRuntimeInvisibleAnnotationAttrInfo(         ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo          runtimeInvisibleAnnotationsAttrInfo);
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(  ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo   runtimeVisibleParameterAnnotationsAttrInfo);
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo);
+    public void visitAnnotationDefaultAttrInfo(                  ClassFile classFile, AnnotationDefaultAttrInfo                    annotationDefaultAttrInfo);
+}
diff --git a/src/proguard/classfile/attribute/CodeAttrInfo.java b/src/proguard/classfile/attribute/CodeAttrInfo.java
new file mode 100644
index 0000000..9e89d83
--- /dev/null
+++ b/src/proguard/classfile/attribute/CodeAttrInfo.java
@@ -0,0 +1,190 @@
+/* $Id: CodeAttrInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of a code attribute.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class CodeAttrInfo extends AttrInfo
+{
+    private static final int CONSTANT_FIELD_SIZE = 12;
+
+
+    public int             u2maxStack;
+    public int             u2maxLocals;
+    public int             u4codeLength;
+    public byte[]          code;
+    public int             u2exceptionTableLength;
+    public ExceptionInfo[] exceptionTable;
+    public int             u2attributesCount;
+    public AttrInfo[]      attributes;
+
+
+    protected CodeAttrInfo()
+    {
+    }
+
+
+    /**
+     * Returns the (first) attribute with the given name.
+     */
+    public AttrInfo getAttribute(ClassFile classFile, String name)
+    {
+        for (int i = 0; i < u2attributesCount; i++)
+        {
+            AttrInfo attribute = attributes[i];
+            if (attribute.getAttributeName(classFile).equals(name))
+            {
+                return attribute;
+            }
+        }
+
+        return null;
+    }
+
+
+    // Implementations for AttrInfo.
+
+    protected int getLength()
+    {
+        int length = CONSTANT_FIELD_SIZE + u4codeLength +
+                        u2exceptionTableLength * ExceptionInfo.CONSTANT_FIELD_SIZE;
+        for (int i = 0; i < u2attributesCount; i++)
+        {
+            length += AttrInfo.CONSTANT_FIELD_SIZE + attributes[i].getLength();
+        }
+
+        return length;
+    }
+
+    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
+    {
+        u2maxStack = din.readUnsignedShort();
+        u2maxLocals = din.readUnsignedShort();
+        u4codeLength = din.readInt();
+        code = new byte[u4codeLength];
+        din.readFully(code);
+        u2exceptionTableLength = din.readUnsignedShort();
+        exceptionTable = new ExceptionInfo[u2exceptionTableLength];
+        for (int i = 0; i < u2exceptionTableLength; i++)
+        {
+            exceptionTable[i] = ExceptionInfo.create(din);
+        }
+        u2attributesCount = din.readUnsignedShort();
+        attributes = new AttrInfo[u2attributesCount];
+        for (int i = 0; i < u2attributesCount; i++)
+        {
+            attributes[i] = AttrInfo.create(din, classFile);
+        }
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2maxStack);
+        dout.writeShort(u2maxLocals);
+        dout.writeInt(u4codeLength);
+        dout.write(code, 0, u4codeLength);
+        dout.writeShort(u2exceptionTableLength);
+        for (int i = 0; i < u2exceptionTableLength; i++)
+        {
+            exceptionTable[i].write(dout);
+        }
+        dout.writeShort(u2attributesCount);
+        for (int i = 0; i < u2attributesCount; i++)
+        {
+            attributes[i].write(dout);
+        }
+    }
+
+    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitCodeAttrInfo(classFile, null, this);
+    }
+
+    public void accept(ClassFile classFile, MethodInfo methodInfo, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitCodeAttrInfo(classFile, methodInfo, this);
+    }
+
+
+    /**
+     * Applies the given instruction visitor to all instructions.
+     */
+    public void instructionsAccept(ClassFile classFile, MethodInfo methodInfo, InstructionVisitor instructionVisitor)
+    {
+        int offset = 0;
+
+        do
+        {
+            // Note that the instruction is only volatile.
+            Instruction instruction = InstructionFactory.create(code, offset);
+            int length = instruction.length(offset);
+            instruction.accept(classFile, methodInfo, this, offset, instructionVisitor);
+            offset += length;
+        }
+        while (offset < u4codeLength);
+    }
+
+    /**
+     * Applies the given instruction visitor to the given instruction.
+     */
+    public void instructionAccept(ClassFile classFile, MethodInfo methodInfo, InstructionVisitor instructionVisitor, int offset)
+    {
+        Instruction instruction = InstructionFactory.create(code, offset);
+        instruction.accept(classFile, methodInfo, this, offset, instructionVisitor);
+    }
+
+    /**
+     * Applies the given exception visitor to all exceptions.
+     */
+    public void exceptionsAccept(ClassFile classFile, MethodInfo methodInfo, ExceptionInfoVisitor exceptionInfoVisitor)
+    {
+        for (int i = 0; i < u2exceptionTableLength; i++)
+        {
+            // We don't need double dispatching here, since there is only one
+            // type of ExceptionInfo.
+            exceptionInfoVisitor.visitExceptionInfo(classFile, methodInfo, this, exceptionTable[i]);
+        }
+    }
+
+    /**
+     * Applies the given attribute visitor to all attributes.
+     */
+    public void attributesAccept(ClassFile classFile, MethodInfo methodInfo, AttrInfoVisitor attrInfoVisitor)
+    {
+        for (int i = 0; i < u2attributesCount; i++)
+        {
+            attributes[i].accept(classFile, methodInfo, this, attrInfoVisitor);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/ConstantValueAttrInfo.java b/src/proguard/classfile/attribute/ConstantValueAttrInfo.java
new file mode 100644
index 0000000..86e937c
--- /dev/null
+++ b/src/proguard/classfile/attribute/ConstantValueAttrInfo.java
@@ -0,0 +1,70 @@
+/* $Id: ConstantValueAttrInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of a constant value attribute.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class ConstantValueAttrInfo extends AttrInfo
+{
+    private static final int CONSTANT_FIELD_SIZE = 2;
+
+
+    public int u2constantValueIndex;
+
+
+    protected ConstantValueAttrInfo()
+    {
+    }
+
+
+    // Implementations for AttrInfo.
+
+    protected int getLength()
+    {
+        return CONSTANT_FIELD_SIZE;
+    }
+
+    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
+    {
+        u2constantValueIndex = din.readUnsignedShort();
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2constantValueIndex);
+    }
+
+    public void accept(ClassFile classFile, FieldInfo fieldInfo, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitConstantValueAttrInfo(classFile, fieldInfo, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/DeprecatedAttrInfo.java b/src/proguard/classfile/attribute/DeprecatedAttrInfo.java
new file mode 100644
index 0000000..d45d692
--- /dev/null
+++ b/src/proguard/classfile/attribute/DeprecatedAttrInfo.java
@@ -0,0 +1,65 @@
+/* $Id: DeprecatedAttrInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of a deprecated attribute.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class DeprecatedAttrInfo extends AttrInfo
+{
+    private static final int CONSTANT_FIELD_SIZE = 0;
+
+
+    protected DeprecatedAttrInfo()
+    {
+    }
+
+
+    // Implementations for AttrInfo.
+
+    protected int getLength()
+    {
+        return CONSTANT_FIELD_SIZE;
+    }
+
+    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
+    {
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+    }
+
+    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitDeprecatedAttrInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/EnclosingMethodAttrInfo.java b/src/proguard/classfile/attribute/EnclosingMethodAttrInfo.java
new file mode 100644
index 0000000..1587550
--- /dev/null
+++ b/src/proguard/classfile/attribute/EnclosingMethodAttrInfo.java
@@ -0,0 +1,135 @@
+/* $Id: EnclosingMethodAttrInfo.java,v 1.2 2004/10/31 18:13:38 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+
+/**
+ * Representation of an enclosing method attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class EnclosingMethodAttrInfo extends AttrInfo
+{
+    private static final int CONSTANT_FIELD_SIZE = 4;
+
+
+    public int u2classIndex;
+    public int u2nameAndTypeIndex;
+
+    /**
+     * An extra field pointing to the referenced ClassFile object.
+     * This field is typically filled out by the <code>{@link
+     * ClassFileReferenceInitializer}</code>.
+     */
+    public ClassFile referencedClassFile;
+
+    /**
+     * An extra field optionally pointing to the referenced MethodInfo object.
+     * This field is typically filled out by the <code>{@link
+     * ClassFileReferenceInitializer}</code>.
+     */
+    public MethodInfo referencedMethodInfo;
+
+
+    protected EnclosingMethodAttrInfo()
+    {
+    }
+
+
+    /**
+     * Returns the class name.
+     */
+    public String getClassName(ClassFile classFile)
+    {
+        return classFile.getCpClassNameString(u2classIndex);
+    }
+
+    /**
+     * Returns the method/field name.
+     */
+    public String getName(ClassFile classFile)
+    {
+        return classFile.getCpNameString(u2nameAndTypeIndex);
+    }
+
+    /**
+     * Returns the type.
+     */
+    public String getType(ClassFile classFile)
+    {
+        return classFile.getCpTypeString(u2nameAndTypeIndex);
+    }
+
+
+    /**
+     * Lets the referenced class file accept the given visitor.
+     */
+    public void referencedClassAccept(ClassFileVisitor classFileVisitor)
+    {
+        if (referencedClassFile != null)
+        {
+            referencedClassFile.accept(classFileVisitor);
+        }
+    }
+
+
+    /**
+     * Lets the referenced class member accept the given visitor.
+     */
+    public void referencedMethodInfoAccept(MemberInfoVisitor memberInfoVisitor)
+    {
+        if (referencedMethodInfo != null)
+        {
+            referencedMethodInfo.accept(referencedClassFile,
+                                        memberInfoVisitor);
+        }
+    }
+
+
+    // Implementations for AttrInfo.
+
+    protected int getLength()
+    {
+        return CONSTANT_FIELD_SIZE;
+    }
+
+    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
+    {
+        u2classIndex       = din.readUnsignedShort();
+        u2nameAndTypeIndex = din.readUnsignedShort();
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2classIndex);
+        dout.writeShort(u2nameAndTypeIndex);
+    }
+
+    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitEnclosingMethodAttrInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/ExceptionInfo.java b/src/proguard/classfile/attribute/ExceptionInfo.java
new file mode 100644
index 0000000..b289343
--- /dev/null
+++ b/src/proguard/classfile/attribute/ExceptionInfo.java
@@ -0,0 +1,91 @@
+/* $Id: ExceptionInfo.java,v 1.2 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of an Exception table entry.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class ExceptionInfo implements VisitorAccepter
+{
+    public static final int CONSTANT_FIELD_SIZE = 8;
+
+
+    public int u2startpc;
+    public int u2endpc;
+    public int u2handlerpc;
+    public int u2catchType;
+
+    /**
+     * An extra field in which visitors can store information.
+     */
+    public Object visitorInfo;
+
+
+    public static ExceptionInfo create(DataInput din) throws IOException
+    {
+        ExceptionInfo ei = new ExceptionInfo();
+        ei.read(din);
+        return ei;
+    }
+
+
+    private ExceptionInfo() {}
+
+    private void read(DataInput din) throws IOException
+    {
+        u2startpc   = din.readUnsignedShort();
+        u2endpc     = din.readUnsignedShort();
+        u2handlerpc = din.readUnsignedShort();
+        u2catchType = din.readUnsignedShort();
+    }
+
+    /**
+     * Exports the representation to a DataOutput stream.
+     */
+    public void write(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2startpc);
+        dout.writeShort(u2endpc);
+        dout.writeShort(u2handlerpc);
+        dout.writeShort(u2catchType);
+    }
+
+
+    // Implementations for VisitorAccepter.
+
+    public Object getVisitorInfo()
+    {
+        return visitorInfo;
+    }
+
+    public void setVisitorInfo(Object visitorInfo)
+    {
+        this.visitorInfo = visitorInfo;
+    }
+}
diff --git a/src/proguard/classfile/attribute/ExceptionInfoVisitor.java b/src/proguard/classfile/attribute/ExceptionInfoVisitor.java
new file mode 100644
index 0000000..e692133
--- /dev/null
+++ b/src/proguard/classfile/attribute/ExceptionInfoVisitor.java
@@ -0,0 +1,36 @@
+/* $Id: ExceptionInfoVisitor.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+
+/**
+ * This interface specifies the methods for a visitor of
+ * <code>ExceptionInfo</code> objects. Note that there is only a single
+ * implementation of <code>ExceptionInfo</code>, such that this interface
+ * is not strictly necessary as a visitor.
+ *
+ * @author Eric Lafortune
+ */
+public interface ExceptionInfoVisitor
+{
+    public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo);
+}
diff --git a/src/proguard/classfile/attribute/ExceptionsAttrInfo.java b/src/proguard/classfile/attribute/ExceptionsAttrInfo.java
new file mode 100644
index 0000000..d465543
--- /dev/null
+++ b/src/proguard/classfile/attribute/ExceptionsAttrInfo.java
@@ -0,0 +1,99 @@
+/* $Id: ExceptionsAttrInfo.java,v 1.2 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of an exceptions attribute.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class ExceptionsAttrInfo extends AttrInfo
+{
+    private static final int CONSTANT_FIELD_SIZE = 2;
+
+
+    public int   u2numberOfExceptions;
+    public int[] u2exceptionIndexTable;
+
+
+    protected ExceptionsAttrInfo()
+    {
+    }
+
+
+    // Implementations for AttrInfo.
+
+    protected int getLength()
+    {
+        return CONSTANT_FIELD_SIZE + 2 * u2numberOfExceptions;
+    }
+
+    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
+    {
+        u2numberOfExceptions = din.readUnsignedShort();
+        u2exceptionIndexTable = new int[u2numberOfExceptions];
+        for (int i = 0; i < u2numberOfExceptions; i++)
+        {
+            u2exceptionIndexTable[i] = din.readUnsignedShort();
+        }
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2numberOfExceptions);
+        for (int i = 0; i < u2numberOfExceptions; i++)
+        {
+            dout.writeShort(u2exceptionIndexTable[i]);
+        }
+    }
+
+    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
+    {
+        // We'll just ignore Exception attributes that do not belong to a method.
+    }
+
+    public void accept(ClassFile classFile, MethodInfo methodInfo, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitExceptionsAttrInfo(classFile, methodInfo, this);
+    }
+
+
+    /**
+     * Applies the given constant pool visitor to all exception class pool info
+     * entries.
+     */
+    public void exceptionEntriesAccept(ProgramClassFile programClassFile, CpInfoVisitor cpInfoVisitor)
+    {
+        for (int i = 0; i < u2numberOfExceptions; i++)
+        {
+            programClassFile.constantPoolEntryAccept(u2exceptionIndexTable[i],
+                                                     cpInfoVisitor);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/InnerClassesAttrInfo.java b/src/proguard/classfile/attribute/InnerClassesAttrInfo.java
new file mode 100644
index 0000000..174a028
--- /dev/null
+++ b/src/proguard/classfile/attribute/InnerClassesAttrInfo.java
@@ -0,0 +1,104 @@
+/* $Id: InnerClassesAttrInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of an inner classes attribute.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class InnerClassesAttrInfo extends AttrInfo
+{
+    private static final int CONSTANT_FIELD_SIZE = 2;
+
+
+    public int                u2numberOfClasses;
+    public InnerClassesInfo[] classes;
+
+
+    protected InnerClassesAttrInfo()
+    {
+    }
+
+
+    /**
+     * Returns the array of inner classes data.
+     */
+    protected InnerClassesInfo[] getInfo() throws Exception
+    {
+        return classes;
+    }
+
+
+    // Implementations for AttrInfo.
+
+    protected int getLength()
+    {
+        return CONSTANT_FIELD_SIZE +
+               u2numberOfClasses * InnerClassesInfo.CONSTANT_FIELD_SIZE;
+    }
+
+    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
+    {
+        u2numberOfClasses = din.readUnsignedShort();
+        classes = new InnerClassesInfo[u2numberOfClasses];
+        for (int i = 0; i < u2numberOfClasses; i++)
+        {
+            classes[i] = InnerClassesInfo.create(din);
+        }
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2numberOfClasses);
+        for (int i = 0; i < u2numberOfClasses; i++)
+        {
+            classes[i].write(dout);
+        }
+    }
+
+    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitInnerClassesAttrInfo(classFile, this);
+    }
+
+
+    /**
+     * Applies the given visitor to all inner classes.
+     */
+    public void innerClassEntriesAccept(ClassFile classFile, InnerClassesInfoVisitor innerClassesInfoVisitor)
+    {
+        for (int i = 0; i < u2numberOfClasses; i++)
+        {
+            // We don't need double dispatching here, since there is only one
+            // type of InnerClassesInfo.
+            innerClassesInfoVisitor.visitInnerClassesInfo(classFile, classes[i]);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/InnerClassesInfo.java b/src/proguard/classfile/attribute/InnerClassesInfo.java
new file mode 100644
index 0000000..51b3bb0
--- /dev/null
+++ b/src/proguard/classfile/attribute/InnerClassesInfo.java
@@ -0,0 +1,112 @@
+/* $Id: InnerClassesInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of an Inner Classes table entry.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class InnerClassesInfo implements VisitorAccepter
+{
+    public static final int CONSTANT_FIELD_SIZE = 8;
+
+
+    public int u2innerClassInfoIndex;
+    public int u2outerClassInfoIndex;
+    public int u2innerNameIndex;
+    public int u2innerClassAccessFlags;
+
+    /**
+     * An extra field in which visitors can store information.
+     */
+    public Object visitorInfo;
+
+
+    public static InnerClassesInfo create(DataInput din) throws IOException
+    {
+        InnerClassesInfo ici = new InnerClassesInfo();
+        ici.read(din);
+        return ici;
+    }
+
+    /**
+     * Returns the inner class index.
+     */
+    protected int getInnerClassIndex()
+    {
+        return u2innerClassInfoIndex;
+    }
+
+    /**
+     * Returns the name index.
+     */
+    protected int getInnerNameIndex()
+    {
+        return u2innerNameIndex;
+    }
+
+    /**
+     * Sets the name index.
+     */
+    protected void setInnerNameIndex(int index)
+    {
+        u2innerNameIndex = index;
+    }
+
+    private void read(DataInput din) throws IOException
+    {
+        u2innerClassInfoIndex   = din.readUnsignedShort();
+        u2outerClassInfoIndex   = din.readUnsignedShort();
+        u2innerNameIndex        = din.readUnsignedShort();
+        u2innerClassAccessFlags = din.readUnsignedShort();
+    }
+
+    /**
+     * Exports the representation to a DataOutput stream.
+     */
+    public void write(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2innerClassInfoIndex);
+        dout.writeShort(u2outerClassInfoIndex);
+        dout.writeShort(u2innerNameIndex);
+        dout.writeShort(u2innerClassAccessFlags);
+    }
+
+
+    // Implementations for VisitorAccepter.
+
+    public Object getVisitorInfo()
+    {
+        return visitorInfo;
+    }
+
+    public void setVisitorInfo(Object visitorInfo)
+    {
+        this.visitorInfo = visitorInfo;
+    }
+}
diff --git a/src/proguard/classfile/attribute/InnerClassesInfoVisitor.java b/src/proguard/classfile/attribute/InnerClassesInfoVisitor.java
new file mode 100644
index 0000000..aa3a7b8
--- /dev/null
+++ b/src/proguard/classfile/attribute/InnerClassesInfoVisitor.java
@@ -0,0 +1,38 @@
+/* $Id: InnerClassesInfoVisitor.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+
+
+/**
+ * This interface specifies the methods for a visitor of
+ * <code>InnerClassesInfo</code> objects. Note that there is only a single
+ * implementation of <code>InnerClassesInfo</code>, such that this interface
+ * is not strictly necessary as a visitor.
+ *
+ * @author Eric Lafortune
+ */
+public interface InnerClassesInfoVisitor
+{
+    public void visitInnerClassesInfo(ClassFile classFile, InnerClassesInfo innerClassesInfo);
+}
diff --git a/src/proguard/classfile/attribute/LibraryAttrInfo.java b/src/proguard/classfile/attribute/LibraryAttrInfo.java
new file mode 100644
index 0000000..579ce04
--- /dev/null
+++ b/src/proguard/classfile/attribute/LibraryAttrInfo.java
@@ -0,0 +1,45 @@
+/* $Id: LibraryAttrInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import java.io.*;
+
+/**
+ * Representation of a light-weight library attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class LibraryAttrInfo
+{
+    /**
+     * Skips LibraryAttrInfo from the data passed.
+     *
+     * @throws java.io.IOException if class file is corrupt or incomplete
+     */
+    public static void skip(DataInput din) throws IOException
+    {
+        // Instantiate based on attribute name
+        int u2attrNameIndex = din.readUnsignedShort();
+        int u4attrLength    = din.readInt();
+        din.skipBytes(u4attrLength);
+    }
+}
diff --git a/src/proguard/classfile/attribute/LineNumberInfo.java b/src/proguard/classfile/attribute/LineNumberInfo.java
new file mode 100644
index 0000000..cc48492
--- /dev/null
+++ b/src/proguard/classfile/attribute/LineNumberInfo.java
@@ -0,0 +1,64 @@
+/* $Id: LineNumberInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import java.io.*;
+
+/**
+ * Representation of an Line Number table entry.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class LineNumberInfo
+{
+    public static final int CONSTANT_FIELD_SIZE = 4;
+
+
+    public int u2startpc;
+    public int u2lineNumber;
+
+
+    public static LineNumberInfo create(DataInput din) throws IOException
+    {
+        LineNumberInfo lni = new LineNumberInfo();
+        lni.read(din);
+        return lni;
+    }
+
+
+    private LineNumberInfo() {}
+    private void read(DataInput din) throws IOException
+    {
+        u2startpc    = din.readUnsignedShort();
+        u2lineNumber = din.readUnsignedShort();
+    }
+
+    /**
+     * Exports the representation to a DataOutput stream.
+     */
+    public void write(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2startpc);
+        dout.writeShort(u2lineNumber);
+    }
+}
diff --git a/src/proguard/classfile/attribute/LineNumberTableAttrInfo.java b/src/proguard/classfile/attribute/LineNumberTableAttrInfo.java
new file mode 100644
index 0000000..48d0d4b
--- /dev/null
+++ b/src/proguard/classfile/attribute/LineNumberTableAttrInfo.java
@@ -0,0 +1,116 @@
+/* $Id: LineNumberTableAttrInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of a line number table attribute.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class LineNumberTableAttrInfo extends AttrInfo
+{
+    private static final int CONSTANT_FIELD_SIZE = 2;
+
+
+    public int              u2lineNumberTableLength;
+    public LineNumberInfo[] lineNumberTable;
+
+
+    protected LineNumberTableAttrInfo()
+    {
+    }
+
+
+    /**
+     * Returns the line number corresponding to the given byte code program
+     * counter.
+     */
+    public int getLineNumber(int pc)
+    {
+        for (int i = u2lineNumberTableLength-1 ; i >= 0 ; i--)
+        {
+            LineNumberInfo info = lineNumberTable[i];
+            if (pc >= info.u2startpc)
+            {
+                return info.u2lineNumber;
+            }
+        }
+
+        return u2lineNumberTableLength > 0 ?
+            lineNumberTable[0].u2lineNumber :
+            0;
+    }
+
+
+    // Implementations for AttrInfo.
+
+    protected int getLength()
+    {
+        return CONSTANT_FIELD_SIZE +
+               u2lineNumberTableLength * LineNumberInfo.CONSTANT_FIELD_SIZE;
+    }
+
+    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
+    {
+        u2lineNumberTableLength = din.readUnsignedShort();
+        lineNumberTable = new LineNumberInfo[u2lineNumberTableLength];
+        for (int i = 0; i < u2lineNumberTableLength; i++)
+        {
+            lineNumberTable[i] = LineNumberInfo.create(din);
+        }
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2lineNumberTableLength);
+        for (int i = 0; i < u2lineNumberTableLength; i++)
+        {
+            lineNumberTable[i].write(dout);
+        }
+    }
+
+    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitLineNumberTableAttrInfo(classFile, methodInfo, codeAttrInfo, this);
+    }
+
+
+    /**
+     * Applies the given visitor to all line numbers.
+     */
+    public void lineNumbersAccept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberInfoVisitor lineNumberInfoVisitor)
+    {
+        for (int i = 0; i < u2lineNumberTableLength; i++)
+        {
+            // We don't need double dispatching here, since there is only one
+            // type of LineNumberInfo.
+            lineNumberInfoVisitor.visitLineNumberInfo(classFile, methodInfo, codeAttrInfo, lineNumberTable[i]);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/LocalVariableInfo.java b/src/proguard/classfile/attribute/LocalVariableInfo.java
new file mode 100644
index 0000000..0108794
--- /dev/null
+++ b/src/proguard/classfile/attribute/LocalVariableInfo.java
@@ -0,0 +1,112 @@
+/* $Id: LocalVariableInfo.java,v 1.2 2004/11/14 00:54:38 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.ClassFile;
+
+import java.io.*;
+
+/**
+ * Representation of an Local Variable table entry.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class LocalVariableInfo
+{
+    public static final int CONSTANT_FIELD_SIZE = 10;
+
+
+    public int u2startpc;
+    public int u2length;
+    public int u2nameIndex;
+    public int u2descriptorIndex;
+    public int u2index;
+
+    /**
+     * An extra field pointing to the referenced ClassFile object.
+     * This field is filled out by the <code>{@link
+     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
+     */
+    public ClassFile referencedClassFile;
+
+
+    public static LocalVariableInfo create(DataInput din) throws IOException
+    {
+        LocalVariableInfo lvi = new LocalVariableInfo();
+        lvi.read(din);
+        return lvi;
+    }
+
+    /**
+     * Returns name index into Constant Pool.
+     */
+    protected int getNameIndex()
+    {
+        return u2nameIndex;
+    }
+
+    /**
+     * Sets the name index.
+     */
+    protected void setNameIndex(int index)
+    {
+        u2nameIndex = index;
+    }
+
+    /**
+     * Returns descriptor index into Constant Pool.
+     */
+    protected int getDescriptorIndex()
+    {
+        return u2descriptorIndex;
+    }
+
+    /**
+     * Sets the descriptor index.
+     */
+    protected void setDescriptorIndex(int index)
+    {
+        u2descriptorIndex = index;
+    }
+
+    private void read(DataInput din) throws IOException
+    {
+        u2startpc         = din.readUnsignedShort();
+        u2length          = din.readUnsignedShort();
+        u2nameIndex       = din.readUnsignedShort();
+        u2descriptorIndex = din.readUnsignedShort();
+        u2index           = din.readUnsignedShort();
+    }
+
+    /**
+     * Exports the representation to a DataOutput stream.
+     */
+    public void write(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2startpc);
+        dout.writeShort(u2length);
+        dout.writeShort(u2nameIndex);
+        dout.writeShort(u2descriptorIndex);
+        dout.writeShort(u2index);
+    }
+}
diff --git a/src/proguard/classfile/attribute/LocalVariableInfoVisitor.java b/src/proguard/classfile/attribute/LocalVariableInfoVisitor.java
new file mode 100644
index 0000000..088675b
--- /dev/null
+++ b/src/proguard/classfile/attribute/LocalVariableInfoVisitor.java
@@ -0,0 +1,38 @@
+/* $Id: LocalVariableInfoVisitor.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+
+
+/**
+ * This interface specifies the methods for a visitor of
+ * <code>LocalVariableInfo</code> objects. Note that there is only a single
+ * implementation of <code>LocalVariableInfo</code>, such that this interface
+ * is not strictly necessary as a visitor.
+ *
+ * @author Eric Lafortune
+ */
+public interface LocalVariableInfoVisitor
+{
+    public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo);
+}
diff --git a/src/proguard/classfile/attribute/LocalVariableTableAttrInfo.java b/src/proguard/classfile/attribute/LocalVariableTableAttrInfo.java
new file mode 100644
index 0000000..107b806
--- /dev/null
+++ b/src/proguard/classfile/attribute/LocalVariableTableAttrInfo.java
@@ -0,0 +1,102 @@
+/* $Id: LocalVariableTableAttrInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of a local variable table attribute.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class LocalVariableTableAttrInfo extends AttrInfo
+{
+    private static final int CONSTANT_FIELD_SIZE = 2;
+
+
+    public int                 u2localVariableTableLength;
+    public LocalVariableInfo[] localVariableTable;
+
+
+    protected LocalVariableTableAttrInfo()
+    {
+    }
+
+
+    /**
+     * Returns the array of local variable table entries.
+     */
+    protected LocalVariableInfo[] getLocalVariableTable() throws Exception
+    {
+        return localVariableTable;
+    }
+
+
+    // Implementations for AttrInfo.
+
+    protected int getLength()
+    {
+        return CONSTANT_FIELD_SIZE +
+               u2localVariableTableLength * LocalVariableInfo.CONSTANT_FIELD_SIZE;
+    }
+
+    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
+    {
+        u2localVariableTableLength = din.readUnsignedShort();
+        localVariableTable = new LocalVariableInfo[u2localVariableTableLength];
+        for (int i = 0; i < u2localVariableTableLength; i++)
+        {
+            localVariableTable[i] = LocalVariableInfo.create(din);
+        }
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2localVariableTableLength);
+        for (int i = 0; i < u2localVariableTableLength; i++)
+        {
+            localVariableTable[i].write(dout);
+        }
+    }
+
+    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitLocalVariableTableAttrInfo(classFile, methodInfo, codeAttrInfo, this);
+    }
+
+
+    /**
+     * Applies the given visitor to all local variables.
+     */
+    public void localVariablesAccept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfoVisitor localVariableInfoVisitor)
+    {
+        for (int i = 0; i < u2localVariableTableLength; i++)
+        {
+            // We don't need double dispatching here, since there is only one
+            // type of LocalVariableInfo.
+            localVariableInfoVisitor.visitLocalVariableInfo(classFile, methodInfo, codeAttrInfo, localVariableTable[i]);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/LocalVariableTypeInfo.java b/src/proguard/classfile/attribute/LocalVariableTypeInfo.java
new file mode 100644
index 0000000..5453992
--- /dev/null
+++ b/src/proguard/classfile/attribute/LocalVariableTypeInfo.java
@@ -0,0 +1,113 @@
+/* $Id: LocalVariableTypeInfo.java,v 1.2 2004/11/14 00:54:38 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.ClassFile;
+
+import java.io.*;
+
+/**
+ * Representation of an Local Variable table entry.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class LocalVariableTypeInfo
+{
+    public static final int CONSTANT_FIELD_SIZE = 10;
+
+
+    public int u2startpc;
+    public int u2length;
+    public int u2nameIndex;
+    public int u2signatureIndex;
+    public int u2index;
+
+    /**
+     * An extra field pointing to the ClassFile objects referenced in the
+     * type string. This field is filled out by the <code>{@link
+     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
+     * References to primitive types are ignored.
+     */
+    public ClassFile[] referencedClassFiles;
+
+
+    public static LocalVariableTypeInfo create(DataInput din) throws IOException
+    {
+        LocalVariableTypeInfo lvi = new LocalVariableTypeInfo();
+        lvi.read(din);
+        return lvi;
+    }
+
+    /**
+     * Returns name index into Constant Pool.
+     */
+    protected int getNameIndex()
+    {
+        return u2nameIndex;
+    }
+
+    /**
+     * Sets the name index.
+     */
+    protected void setNameIndex(int index)
+    {
+        u2nameIndex = index;
+    }
+
+    /**
+     * Returns descriptor index into Constant Pool.
+     */
+    protected int getDescriptorIndex()
+    {
+        return u2signatureIndex;
+    }
+
+    /**
+     * Sets the descriptor index.
+     */
+    protected void setDescriptorIndex(int index)
+    {
+        u2signatureIndex = index;
+    }
+
+    private void read(DataInput din) throws IOException
+    {
+        u2startpc = din.readUnsignedShort();
+        u2length = din.readUnsignedShort();
+        u2nameIndex = din.readUnsignedShort();
+        u2signatureIndex = din.readUnsignedShort();
+        u2index = din.readUnsignedShort();
+    }
+
+    /**
+     * Exports the representation to a DataOutput stream.
+     */
+    public void write(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2startpc);
+        dout.writeShort(u2length);
+        dout.writeShort(u2nameIndex);
+        dout.writeShort(u2signatureIndex);
+        dout.writeShort(u2index);
+    }
+}
diff --git a/src/proguard/classfile/attribute/LocalVariableTypeInfoVisitor.java b/src/proguard/classfile/attribute/LocalVariableTypeInfoVisitor.java
new file mode 100644
index 0000000..f3666c4
--- /dev/null
+++ b/src/proguard/classfile/attribute/LocalVariableTypeInfoVisitor.java
@@ -0,0 +1,38 @@
+/* $Id: LocalVariableTypeInfoVisitor.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+
+
+/**
+ * This interface specifies the methods for a visitor of
+ * <code>LocalVariableTypeInfo</code> objects. Note that there is only a single
+ * implementation of <code>LocalVariableTypeInfo</code>, such that this interface
+ * is not strictly necessary as a visitor.
+ *
+ * @author Eric Lafortune
+ */
+public interface LocalVariableTypeInfoVisitor
+{
+    public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo);
+}
diff --git a/src/proguard/classfile/attribute/LocalVariableTypeTableAttrInfo.java b/src/proguard/classfile/attribute/LocalVariableTypeTableAttrInfo.java
new file mode 100644
index 0000000..c500a06
--- /dev/null
+++ b/src/proguard/classfile/attribute/LocalVariableTypeTableAttrInfo.java
@@ -0,0 +1,102 @@
+/* $Id: LocalVariableTypeTableAttrInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of a local variable table type attribute.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class LocalVariableTypeTableAttrInfo extends AttrInfo
+{
+    private static final int CONSTANT_FIELD_SIZE = 2;
+
+
+    public int                     u2localVariableTypeTableLength;
+    public LocalVariableTypeInfo[] localVariableTypeTable;
+
+
+    protected LocalVariableTypeTableAttrInfo()
+    {
+    }
+
+
+    /**
+     * Returns the array of local variable type table entries.
+     */
+    protected LocalVariableTypeInfo[] getLocalVariableTypeTable() throws Exception
+    {
+        return localVariableTypeTable;
+    }
+
+
+    // Implementations for AttrInfo.
+
+    protected int getLength()
+    {
+        return CONSTANT_FIELD_SIZE +
+               u2localVariableTypeTableLength * LocalVariableTypeInfo.CONSTANT_FIELD_SIZE;
+    }
+
+    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
+    {
+        u2localVariableTypeTableLength = din.readUnsignedShort();
+        localVariableTypeTable = new LocalVariableTypeInfo[u2localVariableTypeTableLength];
+        for (int i = 0; i < u2localVariableTypeTableLength; i++)
+        {
+            localVariableTypeTable[i] = LocalVariableTypeInfo.create(din);
+        }
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2localVariableTypeTableLength);
+        for (int i = 0; i < u2localVariableTypeTableLength; i++)
+        {
+            localVariableTypeTable[i].write(dout);
+        }
+    }
+
+    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitLocalVariableTypeTableAttrInfo(classFile, methodInfo, codeAttrInfo, this);
+    }
+
+
+    /**
+     * Applies the given visitor to all local variable types.
+     */
+    public void localVariablesAccept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfoVisitor localVariableTypeInfoVisitor)
+    {
+        for (int i = 0; i < u2localVariableTypeTableLength; i++)
+        {
+            // We don't need double dispatching here, since there is only one
+            // type of LocalVariableTypeInfo.
+            localVariableTypeInfoVisitor.visitLocalVariableTypeInfo(classFile, methodInfo, codeAttrInfo, localVariableTypeTable[i]);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/MultiAttrInfoVisitor.java b/src/proguard/classfile/attribute/MultiAttrInfoVisitor.java
new file mode 100644
index 0000000..5fcbcfa
--- /dev/null
+++ b/src/proguard/classfile/attribute/MultiAttrInfoVisitor.java
@@ -0,0 +1,234 @@
+/* $Id: MultiAttrInfoVisitor.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+
+/**
+ * This AttrInfoVisitor delegates all visits to each AttrInfoVisitor
+ * in a given list.
+ *
+ * @author Eric Lafortune
+ */
+public class MultiAttrInfoVisitor implements AttrInfoVisitor
+{
+    private static final int ARRAY_SIZE_INCREMENT = 5;
+
+    private AttrInfoVisitor[] attrInfoVisitors;
+    private int               attrInfoVisitorCount;
+
+
+    public MultiAttrInfoVisitor()
+    {
+    }
+
+
+    public MultiAttrInfoVisitor(AttrInfoVisitor[] attrInfoVisitors)
+    {
+        this.attrInfoVisitors     = attrInfoVisitors;
+        this.attrInfoVisitorCount = attrInfoVisitors.length;
+    }
+
+
+    public void addAttrInfoVisitor(AttrInfoVisitor attrInfoVisitor)
+    {
+        ensureArraySize();
+
+        attrInfoVisitors[attrInfoVisitorCount++] = attrInfoVisitor;
+    }
+
+
+    private void ensureArraySize()
+    {
+        if (attrInfoVisitors == null)
+        {
+            attrInfoVisitors = new AttrInfoVisitor[ARRAY_SIZE_INCREMENT];
+        }
+        else if (attrInfoVisitors.length == attrInfoVisitorCount)
+        {
+            AttrInfoVisitor[] newAttrInfoVisitors =
+                new AttrInfoVisitor[attrInfoVisitorCount +
+                                     ARRAY_SIZE_INCREMENT];
+            System.arraycopy(attrInfoVisitors, 0,
+                             newAttrInfoVisitors, 0,
+                             attrInfoVisitorCount);
+            attrInfoVisitors = newAttrInfoVisitors;
+        }
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitUnknownAttrInfo(classFile, unknownAttrInfo);
+        }
+    }
+
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitInnerClassesAttrInfo(classFile, innerClassesAttrInfo);
+        }
+    }
+
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitEnclosingMethodAttrInfo(classFile, enclosingMethodAttrInfo);
+        }
+    }
+
+
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitConstantValueAttrInfo(classFile, fieldInfo, constantValueAttrInfo);
+        }
+    }
+
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitExceptionsAttrInfo(classFile, methodInfo, exceptionsAttrInfo);
+        }
+    }
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
+        }
+    }
+
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitLineNumberTableAttrInfo(classFile, methodInfo, codeAttrInfo, lineNumberTableAttrInfo);
+        }
+    }
+
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitLocalVariableTableAttrInfo(classFile, methodInfo, codeAttrInfo, localVariableTableAttrInfo);
+        }
+    }
+
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitLocalVariableTypeTableAttrInfo(classFile, methodInfo, codeAttrInfo, localVariableTypeTableAttrInfo);
+        }
+    }
+
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitSourceFileAttrInfo(classFile, sourceFileAttrInfo);
+        }
+    }
+
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitSourceDirAttrInfo(classFile, sourceDirAttrInfo);
+        }
+    }
+
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitDeprecatedAttrInfo(classFile, deprecatedAttrInfo);
+        }
+    }
+
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitSyntheticAttrInfo(classFile, syntheticAttrInfo);
+        }
+    }
+
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo syntheticAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitSignatureAttrInfo(classFile, syntheticAttrInfo);
+        }
+    }
+
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitRuntimeVisibleAnnotationAttrInfo(classFile, runtimeVisibleAnnotationsAttrInfo);
+        }
+    }
+
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitRuntimeInvisibleAnnotationAttrInfo(classFile, runtimeInvisibleAnnotationsAttrInfo);
+        }
+    }
+
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitRuntimeVisibleParameterAnnotationAttrInfo(classFile, runtimeVisibleParameterAnnotationsAttrInfo);
+        }
+    }
+
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitRuntimeInvisibleParameterAnnotationAttrInfo(classFile, runtimeInvisibleParameterAnnotationsAttrInfo);
+        }
+    }
+
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
+    {
+        for (int index = 0; index < attrInfoVisitorCount; index++)
+        {
+            attrInfoVisitors[index].visitAnnotationDefaultAttrInfo(classFile, annotationDefaultAttrInfo);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/proguard/classfile/attribute/SignatureAttrInfo.java b/src/proguard/classfile/attribute/SignatureAttrInfo.java
new file mode 100644
index 0000000..18a7f16
--- /dev/null
+++ b/src/proguard/classfile/attribute/SignatureAttrInfo.java
@@ -0,0 +1,75 @@
+/* $Id: SignatureAttrInfo.java,v 1.2 2004/11/14 00:54:38 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of a signature attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class SignatureAttrInfo extends AttrInfo
+{
+    private static final int CONSTANT_FIELD_SIZE = 2;
+
+
+    public int u2signatureIndex;
+
+    /**
+     * An extra field pointing to the ClassFile objects referenced in the
+     * signature string. This field is filled out by the <code>{@link
+     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
+     * References to primitive types are ignored.
+     */
+    public ClassFile[] referencedClassFiles;
+
+
+    protected SignatureAttrInfo()
+    {
+    }
+
+
+    // Implementations for AttrInfo.
+
+    protected int getLength()
+    {
+        return CONSTANT_FIELD_SIZE;
+    }
+
+    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
+    {
+        u2signatureIndex = din.readUnsignedShort();
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2signatureIndex);
+    }
+
+    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitSignatureAttrInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/SourceDirAttrInfo.java b/src/proguard/classfile/attribute/SourceDirAttrInfo.java
new file mode 100644
index 0000000..1edf2bf
--- /dev/null
+++ b/src/proguard/classfile/attribute/SourceDirAttrInfo.java
@@ -0,0 +1,69 @@
+/* $Id: SourceDirAttrInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of a source directory attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class SourceDirAttrInfo extends AttrInfo
+{
+    private static final int CONSTANT_FIELD_SIZE = 2;
+
+
+    public int u2sourceDirIndex;
+
+
+    protected SourceDirAttrInfo()
+    {
+    }
+
+
+    // Implementations for AttrInfo.
+
+    protected int getLength()
+    {
+        return CONSTANT_FIELD_SIZE;
+    }
+
+    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
+    {
+        u2sourceDirIndex = din.readUnsignedShort();
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2sourceDirIndex);
+    }
+
+    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitSourceDirAttrInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/SourceFileAttrInfo.java b/src/proguard/classfile/attribute/SourceFileAttrInfo.java
new file mode 100644
index 0000000..8f6ff9e
--- /dev/null
+++ b/src/proguard/classfile/attribute/SourceFileAttrInfo.java
@@ -0,0 +1,70 @@
+/* $Id: SourceFileAttrInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of a source file attribute.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class SourceFileAttrInfo extends AttrInfo
+{
+    private static final int CONSTANT_FIELD_SIZE = 2;
+
+
+    public int u2sourceFileIndex;
+
+
+    protected SourceFileAttrInfo()
+    {
+    }
+
+
+    // Implementations for AttrInfo.
+
+    protected int getLength()
+    {
+        return CONSTANT_FIELD_SIZE;
+    }
+
+    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
+    {
+        u2sourceFileIndex = din.readUnsignedShort();
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2sourceFileIndex);
+    }
+
+    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitSourceFileAttrInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/SyntheticAttrInfo.java b/src/proguard/classfile/attribute/SyntheticAttrInfo.java
new file mode 100644
index 0000000..c0fb2f9
--- /dev/null
+++ b/src/proguard/classfile/attribute/SyntheticAttrInfo.java
@@ -0,0 +1,65 @@
+/* $Id: SyntheticAttrInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of a synthetic attribute.
+ *
+ * @author Mark Welsh
+ * @author Eric Lafortune
+ */
+public class SyntheticAttrInfo extends AttrInfo
+{
+    private static final int CONSTANT_FIELD_SIZE = 0;
+
+
+    protected SyntheticAttrInfo()
+    {
+    }
+
+
+    // Implementations for AttrInfo.
+
+    protected int getLength()
+    {
+        return CONSTANT_FIELD_SIZE;
+    }
+
+    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
+    {
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+    }
+
+    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitSyntheticAttrInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/UnknownAttrInfo.java b/src/proguard/classfile/attribute/UnknownAttrInfo.java
new file mode 100644
index 0000000..5811766
--- /dev/null
+++ b/src/proguard/classfile/attribute/UnknownAttrInfo.java
@@ -0,0 +1,70 @@
+/* $Id: UnknownAttrInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+
+import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of an unknown attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class UnknownAttrInfo extends AttrInfo
+{
+    public int  u4attrLength;
+    public byte info[];
+
+
+    protected UnknownAttrInfo(int attrLength)
+    {
+        u4attrLength = attrLength;
+    }
+
+
+    // Implementations for AttrInfo.
+
+    protected int getLength()
+    {
+        return u4attrLength;
+    }
+
+    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
+    {
+        info = new byte[u4attrLength];
+        din.readFully(info);
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.write(info);
+    }
+
+    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitUnknownAttrInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/Annotation.java b/src/proguard/classfile/attribute/annotation/Annotation.java
new file mode 100644
index 0000000..76cea29
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/Annotation.java
@@ -0,0 +1,134 @@
+/* $Id: Annotation.java,v 1.3 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of a runtime annotation.
+ *
+ * @author Eric Lafortune
+ */
+public class Annotation implements VisitorAccepter
+{
+    private static final int CONSTANT_FIELD_SIZE  = 4;
+    private static final int CONSTANT_FIELD_SIZE2 = 2;
+
+
+    public int            u2typeIndex;
+    public int            u2numberOfElementValuePairs;
+    public ElementValue[] elementValues;
+
+    /**
+     * An extra field pointing to the ClassFile objects referenced in the
+     * type string. This field is filled out by the <code>{@link
+     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
+     * References to primitive types are ignored.
+     */
+    public ClassFile[] referencedClassFiles;
+
+    /**
+     * An extra field in which visitors can store information.
+     */
+    public Object visitorInfo;
+
+
+    public static Annotation create(DataInput din) throws IOException
+    {
+        Annotation annotation = new Annotation();
+        annotation.read(din);
+        return annotation;
+    }
+
+    private void read(DataInput din) throws IOException
+    {
+        u2typeIndex                 = din.readUnsignedShort();
+        u2numberOfElementValuePairs = din.readUnsignedShort();
+
+        elementValues = new ElementValue[u2numberOfElementValuePairs];
+
+        for (int i = 0; i < u2numberOfElementValuePairs; i++)
+        {
+            int u2elementName = din.readUnsignedShort();
+
+            elementValues[i] = ElementValue.create(din);
+
+            elementValues[i].u2elementName = u2elementName;
+        }
+    }
+
+    /**
+     * Exports the representation to a DataOutput stream.
+     */
+    public void write(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2typeIndex);
+        dout.writeShort(u2numberOfElementValuePairs);
+        for (int i = 0; i < u2numberOfElementValuePairs; i++)
+        {
+            dout.writeShort(elementValues[i].u2elementName);
+
+            elementValues[i].write(dout);
+        }
+    }
+
+
+    /**
+     * Returns the length of this annotation, expressed in bytes.
+     */
+    protected int getLength()
+    {
+        int length = CONSTANT_FIELD_SIZE;
+        for (int i = 0; i < u2numberOfElementValuePairs; i++)
+        {
+            length += CONSTANT_FIELD_SIZE2 + elementValues[i].getLength();
+        }
+
+        return length;
+    }
+
+
+    /**
+     * Applies the given visitor to all element value pairs.
+     */
+    public void elementValuesAccept(ClassFile classFile, ElementValueVisitor elementValueVisitor)
+    {
+        for (int i = 0; i < u2numberOfElementValuePairs; i++)
+        {
+            elementValues[i].accept(classFile, this, elementValueVisitor);
+        }
+    }
+
+
+    // Implementations for VisitorAccepter.
+
+    public Object getVisitorInfo()
+    {
+        return visitorInfo;
+    }
+
+    public void setVisitorInfo(Object visitorInfo)
+    {
+        this.visitorInfo = visitorInfo;
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttrInfo.java b/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttrInfo.java
new file mode 100644
index 0000000..37925cd
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttrInfo.java
@@ -0,0 +1,77 @@
+/* $Id: AnnotationDefaultAttrInfo.java,v 1.3 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.ClassFile;
+import proguard.classfile.attribute.*;
+
+import java.io.*;
+
+/**
+ * Representation of a runtime visible annotations attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class AnnotationDefaultAttrInfo extends AttrInfo
+{
+    private static final int CONSTANT_FIELD_SIZE = 0;
+
+
+    public ElementValue defaultValue;
+
+
+    public AnnotationDefaultAttrInfo()
+    {
+    }
+
+
+    /**
+     * Applies the given visitor to the default element values.
+     */
+    public void defaultValueAccept(ClassFile classFile, ElementValueVisitor elementValueVisitor)
+    {
+        defaultValue.accept(classFile, null, elementValueVisitor);
+    }
+
+
+    // Implementations for AttrInfo.
+
+    protected int getLength()
+    {
+        return CONSTANT_FIELD_SIZE + defaultValue.getLength();
+    }
+
+    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
+    {
+        defaultValue = ElementValue.create(din);
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        defaultValue.write(dout);
+    }
+
+
+    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitAnnotationDefaultAttrInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java b/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java
new file mode 100644
index 0000000..256c100
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java
@@ -0,0 +1,76 @@
+/* $Id: AnnotationElementValue.java,v 1.3 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of an annotation element value.
+ *
+ * @author Eric Lafortune
+ */
+public class AnnotationElementValue extends ElementValue
+{
+    protected static final int CONSTANT_FIELD_SIZE = ElementValue.CONSTANT_FIELD_SIZE;
+
+
+    public Annotation annotationValue;
+
+
+    protected AnnotationElementValue()
+    {
+    }
+
+
+    /**
+     * Applies the given visitor to the annotation.
+     */
+    public void annotationAccept(ClassFile classFile, AnnotationVisitor annotationVisitor)
+    {
+        annotationVisitor.visitAnnotation(classFile, annotationValue);
+    }
+
+
+    // Implementations for ElementValue.
+
+    protected int getLength()
+    {
+        return CONSTANT_FIELD_SIZE + annotationValue.getLength();
+    }
+
+    protected void readInfo(DataInput din) throws IOException
+    {
+        annotationValue = Annotation.create(din);
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        annotationValue.write(dout);
+    }
+
+    public void accept(ClassFile classFile, Annotation annotation, ElementValueVisitor elementValueVisitor)
+    {
+        elementValueVisitor.visitAnnotationElementValue(classFile, annotation, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/AnnotationVisitor.java b/src/proguard/classfile/attribute/annotation/AnnotationVisitor.java
new file mode 100644
index 0000000..21892ec
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/AnnotationVisitor.java
@@ -0,0 +1,40 @@
+/* $Id: AnnotationVisitor.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+
+
+/**
+ * This interface specifies the methods for a visitor of
+ * <code>Annotation</code> objects. Note that there is only a single
+ * implementation of <code>Annotation</code>, such that this interface
+ * is not strictly necessary as a visitor.
+ *
+ * @author Eric Lafortune
+ */
+public interface AnnotationVisitor
+{
+    public void visitAnnotation(ClassFile classFile, Annotation annotation);
+
+//    public void visitAnnotation(ClassFile classFile, MethodInfo methodInfo, int parameterIndex, Annotation annotation);
+}
diff --git a/src/proguard/classfile/attribute/annotation/ArrayElementValue.java b/src/proguard/classfile/attribute/annotation/ArrayElementValue.java
new file mode 100644
index 0000000..e3ebbcb
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/ArrayElementValue.java
@@ -0,0 +1,95 @@
+/* $Id: ArrayElementValue.java,v 1.2 2004/11/14 00:54:38 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of an array element value.
+ *
+ * @author Eric Lafortune
+ */
+public class ArrayElementValue extends ElementValue
+{
+    protected static final int CONSTANT_FIELD_SIZE = ElementValue.CONSTANT_FIELD_SIZE + 2;
+
+
+    public int            u2numberOfValues;
+    public ElementValue[] values;
+
+
+    protected ArrayElementValue()
+    {
+    }
+
+
+    // Implementations for ElementValue.
+
+    protected int getLength()
+    {
+        int length = CONSTANT_FIELD_SIZE;
+        for (int i = 0; i < u2numberOfValues; i++)
+        {
+            length += values[i].getLength();
+        }
+
+        return length;
+    }
+
+    protected void readInfo(DataInput din) throws IOException
+    {
+        u2numberOfValues = din.readUnsignedShort();
+        values = new ElementValue[u2numberOfValues];
+        for (int i = 0; i < u2numberOfValues; i++)
+        {
+            values[i] = ElementValue.create(din);
+        }
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2numberOfValues);
+        for (int i = 0; i < u2numberOfValues; i++)
+        {
+            values[i].write(dout);
+        }
+    }
+
+    public void accept(ClassFile classFile, Annotation annotation, ElementValueVisitor elementValueVisitor)
+    {
+        elementValueVisitor.visitArrayElementValue(classFile, annotation, this);
+    }
+
+
+    /**
+     * Applies the given visitor to all nested element values.
+     */
+    public void elementValuesAccept(ClassFile classFile, Annotation annotation, ElementValueVisitor elementValueVisitor)
+    {
+        for (int i = 0; i < u2numberOfValues; i++)
+        {
+            values[i].accept(classFile, annotation, elementValueVisitor);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/ClassElementValue.java b/src/proguard/classfile/attribute/annotation/ClassElementValue.java
new file mode 100644
index 0000000..7778a03
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/ClassElementValue.java
@@ -0,0 +1,75 @@
+/* $Id: ClassElementValue.java,v 1.2 2004/11/14 00:54:38 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of a class element value.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassElementValue extends ElementValue
+{
+    protected static final int CONSTANT_FIELD_SIZE = ElementValue.CONSTANT_FIELD_SIZE + 2;
+
+
+    public int u2classInfoIndex;
+
+    /**
+     * An extra field pointing to the ClassFile objects referenced in the
+     * type name string. This field is filled out by the <code>{@link
+     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
+     * References to primitive types are ignored.
+     */
+    public ClassFile[] referencedClassFiles;
+
+
+    protected ClassElementValue()
+    {
+    }
+
+
+    // Implementations for ElementValue.
+
+    protected int getLength()
+    {
+        return CONSTANT_FIELD_SIZE;
+    }
+
+    protected void readInfo(DataInput din) throws IOException
+    {
+        u2classInfoIndex = din.readUnsignedShort();
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2classInfoIndex);
+    }
+
+    public void accept(ClassFile classFile, Annotation annotation, ElementValueVisitor elementValueVisitor)
+    {
+        elementValueVisitor.visitClassElementValue(classFile, annotation, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/ConstantElementValue.java b/src/proguard/classfile/attribute/annotation/ConstantElementValue.java
new file mode 100644
index 0000000..2c3997b
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/ConstantElementValue.java
@@ -0,0 +1,67 @@
+/* $Id: ConstantElementValue.java,v 1.2 2004/11/14 00:54:38 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of a constant element value.
+ *
+ * @author Eric Lafortune
+ */
+public class ConstantElementValue extends ElementValue
+{
+    protected static final int CONSTANT_FIELD_SIZE = ElementValue.CONSTANT_FIELD_SIZE + 2;
+
+
+    public int u2constantValueIndex;
+
+
+    protected ConstantElementValue()
+    {
+    }
+
+
+    // Implementations for ElementValue.
+
+    protected int getLength()
+    {
+        return CONSTANT_FIELD_SIZE;
+    }
+
+    protected void readInfo(DataInput din) throws IOException
+    {
+        u2constantValueIndex = din.readUnsignedShort();
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2constantValueIndex);
+    }
+
+    public void accept(ClassFile classFile, Annotation annotation, ElementValueVisitor elementValueVisitor)
+    {
+        elementValueVisitor.visitConstantElementValue(classFile, annotation, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/ElementValue.java b/src/proguard/classfile/attribute/annotation/ElementValue.java
new file mode 100644
index 0000000..d4754fb
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/ElementValue.java
@@ -0,0 +1,147 @@
+/* $Id: ElementValue.java,v 1.4 2004/11/27 10:09:26 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of an element value.
+ *
+ * @author Eric Lafortune
+ */
+public abstract class ElementValue implements VisitorAccepter
+{
+    protected static final int CONSTANT_FIELD_SIZE = 1;
+
+
+    public int u1tag;
+
+    /**
+     * An extra field for the optional element name. It is used in element value
+     * pairs of annotations. Otherwise, it is 0.
+     */
+    public int u2elementName;
+
+    /**
+     * An extra field pointing to the referenced <code>MethodInfo</code>
+     * object, if applicable. This field is typically filled out by the
+     * <code>{@link ClassFileReferenceInitializer}</code>.
+     */
+    public MethodInfo referencedMethodInfo;
+
+    /**
+     * An extra field in which visitors can store information.
+     */
+    public Object visitorInfo;
+
+
+    public static ElementValue create(DataInput din) throws IOException
+    {
+        int u1tag = din.readUnsignedByte();
+        ElementValue elementValue = createElementValue(u1tag);
+
+        elementValue.u1tag = u1tag;
+        elementValue.readInfo(din);
+
+        return elementValue;
+    }
+
+
+    private static ElementValue createElementValue(int u1tag)
+    {
+        switch (u1tag)
+        {
+            case ClassConstants.INTERNAL_TYPE_BOOLEAN:
+            case ClassConstants.INTERNAL_TYPE_BYTE:
+            case ClassConstants.INTERNAL_TYPE_CHAR:
+            case ClassConstants.INTERNAL_TYPE_SHORT:
+            case ClassConstants.INTERNAL_TYPE_INT:
+            case ClassConstants.INTERNAL_TYPE_FLOAT:
+            case ClassConstants.INTERNAL_TYPE_LONG:
+            case ClassConstants.INTERNAL_TYPE_DOUBLE:
+            case ClassConstants.ELEMENT_VALUE_STRING_CONSTANT: return new ConstantElementValue();
+
+            case ClassConstants.ELEMENT_VALUE_ENUM_CONSTANT:   return new EnumConstantElementValue();
+            case ClassConstants.ELEMENT_VALUE_CLASS:           return new ClassElementValue();
+            case ClassConstants.ELEMENT_VALUE_ANNOTATION:      return new AnnotationElementValue();
+            case ClassConstants.ELEMENT_VALUE_ARRAY:           return new ArrayElementValue();
+        }
+
+        throw new IllegalArgumentException("Unknown element value tag ["+u1tag+"]");
+    }
+
+
+    protected ElementValue()
+    {
+    }
+
+
+    /**
+     * Exports the representation to a DataOutput stream.
+     */
+    public final void write(DataOutput dout) throws IOException
+    {
+        dout.writeByte(u1tag);
+        writeInfo(dout);
+    }
+
+
+    // Abstract methods to be implemented by extensions.
+
+    /**
+     * Returns the length of this element value, expressed in bytes.
+     */
+    protected abstract int getLength();
+
+
+    /**
+     * Reads the data following the header.
+     */
+    protected abstract void readInfo(DataInput din) throws IOException;
+
+
+    /**
+     * Exports data following the header to a DataOutput stream.
+     */
+    protected abstract void writeInfo(DataOutput dout) throws IOException;
+
+
+    /**
+     * Accepts the given visitor.
+     */
+    public abstract void accept(ClassFile classFile, Annotation annotation, ElementValueVisitor elementValueVisitor);
+
+
+
+    // Implementations for VisitorAccepter.
+
+    public Object getVisitorInfo()
+    {
+        return visitorInfo;
+    }
+
+    public void setVisitorInfo(Object visitorInfo)
+    {
+        this.visitorInfo = visitorInfo;
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/ElementValueVisitor.java b/src/proguard/classfile/attribute/annotation/ElementValueVisitor.java
new file mode 100644
index 0000000..2bc4b29
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/ElementValueVisitor.java
@@ -0,0 +1,40 @@
+/* $Id: ElementValueVisitor.java,v 1.2 2004/11/14 00:54:38 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+
+
+/**
+ * This interface specifies the methods for a visitor of <code>ElementValue</code>
+ * objects.
+ *
+ * @author Eric Lafortune
+ */
+public interface ElementValueVisitor
+{
+    public void visitConstantElementValue(    ClassFile classFile, Annotation annotation, ConstantElementValue     constantElementValue);
+    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue);
+    public void visitClassElementValue(       ClassFile classFile, Annotation annotation, ClassElementValue        classElementValue);
+    public void visitAnnotationElementValue(  ClassFile classFile, Annotation annotation, AnnotationElementValue   annotationElementValue);
+    public void visitArrayElementValue(       ClassFile classFile, Annotation annotation, ArrayElementValue        arrayElementValue);
+}
diff --git a/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java b/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java
new file mode 100644
index 0000000..21be5d5
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java
@@ -0,0 +1,78 @@
+/* $Id: EnumConstantElementValue.java,v 1.2 2004/11/14 00:54:38 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * Representation of an enumeration constant element value.
+ *
+ * @author Eric Lafortune
+ */
+public class EnumConstantElementValue extends ElementValue
+{
+    protected static final int CONSTANT_FIELD_SIZE = ElementValue.CONSTANT_FIELD_SIZE + 4;
+
+
+    public int u2typeNameIndex;
+    public int u2constantNameIndex;
+
+    /**
+     * An extra field pointing to the ClassFile objects referenced in the
+     * type name string. This field is filled out by the <code>{@link
+     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
+     * References to primitive types are ignored.
+     */
+    public ClassFile[] referencedClassFiles;
+
+
+    protected EnumConstantElementValue()
+    {
+    }
+
+
+    // Implementations for ElementValue.
+
+    protected int getLength()
+    {
+        return CONSTANT_FIELD_SIZE;
+    }
+
+    protected void readInfo(DataInput din) throws IOException
+    {
+        u2typeNameIndex     = din.readUnsignedShort();
+        u2constantNameIndex = din.readUnsignedShort();
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2typeNameIndex);
+        dout.writeShort(u2constantNameIndex);
+    }
+
+    public void accept(ClassFile classFile, Annotation annotation, ElementValueVisitor elementValueVisitor)
+    {
+        elementValueVisitor.visitEnumConstantElementValue(classFile, annotation, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/RuntimeAnnotationsAttrInfo.java b/src/proguard/classfile/attribute/annotation/RuntimeAnnotationsAttrInfo.java
new file mode 100644
index 0000000..17a48b7
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/RuntimeAnnotationsAttrInfo.java
@@ -0,0 +1,92 @@
+/* $Id: RuntimeAnnotationsAttrInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+
+import java.io.*;
+
+/**
+ * Representation of a runtime annotations attribute.
+ *
+ * @author Eric Lafortune
+ */
+public abstract class RuntimeAnnotationsAttrInfo extends AttrInfo
+{
+    private static final int CONSTANT_FIELD_SIZE = 2;
+
+
+    public int          u2numberOfAnnotations;
+    public Annotation[] annotations;
+
+
+    protected RuntimeAnnotationsAttrInfo()
+    {
+    }
+
+
+    /**
+     * Applies the given visitor to all annotations.
+     */
+    public void annotationsAccept(ClassFile classFile, AnnotationVisitor annotationVisitor)
+    {
+        for (int i = 0; i < u2numberOfAnnotations; i++)
+        {
+            // We don't need double dispatching here, since there is only one
+            // type of Annotation.
+            annotationVisitor.visitAnnotation(classFile, annotations[i]);
+        }
+    }
+
+
+    // Implementations for AttrInfo.
+
+    protected int getLength()
+    {
+        int length = CONSTANT_FIELD_SIZE;
+        for (int i = 0; i < u2numberOfAnnotations; i++)
+        {
+            length += annotations[i].getLength();
+        }
+
+        return length;
+    }
+
+    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
+    {
+        u2numberOfAnnotations = din.readUnsignedShort();
+        annotations = new Annotation[u2numberOfAnnotations];
+        for (int i = 0; i < u2numberOfAnnotations; i++)
+        {
+            annotations[i] = Annotation.create(din);
+        }
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(u2numberOfAnnotations);
+        for (int i = 0; i < u2numberOfAnnotations; i++)
+        {
+            annotations[i].write(dout);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttrInfo.java b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttrInfo.java
new file mode 100644
index 0000000..89d755e
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttrInfo.java
@@ -0,0 +1,44 @@
+/* $Id: RuntimeInvisibleAnnotationsAttrInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.ClassFile;
+import proguard.classfile.attribute.AttrInfoVisitor;
+
+/**
+ * Representation of a runtime invisible annotations attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class RuntimeInvisibleAnnotationsAttrInfo extends RuntimeAnnotationsAttrInfo
+{
+    public RuntimeInvisibleAnnotationsAttrInfo()
+    {
+    }
+
+
+    // Implementations for AttrInfo.
+
+    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitRuntimeInvisibleAnnotationAttrInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttrInfo.java b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttrInfo.java
new file mode 100644
index 0000000..1c5387b
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttrInfo.java
@@ -0,0 +1,44 @@
+/* $Id: RuntimeInvisibleParameterAnnotationsAttrInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.ClassFile;
+import proguard.classfile.attribute.AttrInfoVisitor;
+
+/**
+ * Representation of a runtime invisible parameter annotations attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class RuntimeInvisibleParameterAnnotationsAttrInfo extends RuntimeParameterAnnotationsAttrInfo
+{
+    public RuntimeInvisibleParameterAnnotationsAttrInfo()
+    {
+    }
+
+
+    // Implementations for AttrInfo.
+
+    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitRuntimeInvisibleParameterAnnotationAttrInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/RuntimeParameterAnnotationsAttrInfo.java b/src/proguard/classfile/attribute/annotation/RuntimeParameterAnnotationsAttrInfo.java
new file mode 100644
index 0000000..0c2d191
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/RuntimeParameterAnnotationsAttrInfo.java
@@ -0,0 +1,120 @@
+/* $Id: RuntimeParameterAnnotationsAttrInfo.java,v 1.2 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.AttrInfo;
+
+import java.io.*;
+
+/**
+ * Representation of a runtime parameter annotations attribute.
+ *
+ * @author Eric Lafortune
+ */
+public abstract class RuntimeParameterAnnotationsAttrInfo extends AttrInfo
+{
+    private static final int CONSTANT_FIELD_SIZE = 2;
+
+
+    //public int            u2numberOfParameters;
+    //public int[]          u2numberOfAnnotations;
+    public Annotation[][] parameterAnnotations;
+
+
+    protected RuntimeParameterAnnotationsAttrInfo()
+    {
+    }
+
+
+    /**
+     * Applies the given visitor to all annotations.
+     */
+    public void annotationsAccept(ClassFile classFile, AnnotationVisitor annotationVisitor)
+    {
+        for (int i = 0; i < parameterAnnotations.length; i++)
+        {
+            Annotation[] annotations = parameterAnnotations[i];
+            for (int j = 0; j < annotations.length; i++)
+            {
+                // We don't need double dispatching here, since there is only one
+                // type of Annotation.
+                //annotationVisitor.visitAnnotation(classFile, methodInfo, i, annotations[j]);
+                annotationVisitor.visitAnnotation(classFile, annotations[j]);
+            }
+        }
+    }
+
+
+    // Implementations for AttrInfo.
+
+    protected int getLength()
+    {
+        int length = CONSTANT_FIELD_SIZE;
+
+        for (int i = 0; i < parameterAnnotations.length; i++)
+        {
+            length += CONSTANT_FIELD_SIZE;
+
+            Annotation[] annotations = parameterAnnotations[i];
+            for (int j = 0; j < annotations.length; i++)
+            {
+                length += annotations[j].getLength();
+            }
+        }
+
+        return length;
+    }
+
+    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
+    {
+        int u2numberOfParameters = din.readUnsignedShort();
+        parameterAnnotations = new Annotation[u2numberOfParameters][];
+
+        for (int i = 0; i < u2numberOfParameters; i++)
+        {
+            int u2numberOfAnnotations = din.readUnsignedShort();
+            Annotation[] annotations = new Annotation[u2numberOfAnnotations];
+
+            for (int j = 0; j < u2numberOfAnnotations; j++)
+            {
+                annotations[j] = Annotation.create(din);
+            }
+
+            parameterAnnotations[i] = annotations;
+        }
+    }
+
+    protected void writeInfo(DataOutput dout) throws IOException
+    {
+        dout.writeShort(parameterAnnotations.length);
+        for (int i = 0; i < parameterAnnotations.length; i++)
+        {
+            Annotation[] annotations = parameterAnnotations[i];
+
+            dout.writeShort(annotations.length);
+            for (int j = 0; j < annotations.length; i++)
+            {
+                annotations[j].write(dout);
+            }
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttrInfo.java b/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttrInfo.java
new file mode 100644
index 0000000..9ea8fc8
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttrInfo.java
@@ -0,0 +1,44 @@
+/* $Id: RuntimeVisibleAnnotationsAttrInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.ClassFile;
+import proguard.classfile.attribute.AttrInfoVisitor;
+
+/**
+ * Representation of a runtime visible annotations attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class RuntimeVisibleAnnotationsAttrInfo extends RuntimeAnnotationsAttrInfo
+{
+    public RuntimeVisibleAnnotationsAttrInfo()
+    {
+    }
+
+
+    // Implementations for AttrInfo.
+
+    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitRuntimeVisibleAnnotationAttrInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttrInfo.java b/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttrInfo.java
new file mode 100644
index 0000000..a1ee4a3
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttrInfo.java
@@ -0,0 +1,44 @@
+/* $Id: RuntimeVisibleParameterAnnotationsAttrInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.ClassFile;
+import proguard.classfile.attribute.AttrInfoVisitor;
+
+/**
+ * Representation of a runtime visible parameter annotations attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class RuntimeVisibleParameterAnnotationsAttrInfo extends RuntimeParameterAnnotationsAttrInfo
+{
+    public RuntimeVisibleParameterAnnotationsAttrInfo()
+    {
+    }
+
+
+    // Implementations for AttrInfo.
+
+    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
+    {
+        attrInfoVisitor.visitRuntimeVisibleParameterAnnotationAttrInfo(classFile, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/package.html b/src/proguard/classfile/attribute/annotation/package.html
new file mode 100644
index 0000000..6aacff3
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/package.html
@@ -0,0 +1,4 @@
+<body>
+This package contains classes to represent the annotation attributes inside
+class files.
+</body>
diff --git a/src/proguard/classfile/attribute/package.html b/src/proguard/classfile/attribute/package.html
new file mode 100644
index 0000000..d17caaa
--- /dev/null
+++ b/src/proguard/classfile/attribute/package.html
@@ -0,0 +1,3 @@
+<body>
+This package contains classes to represent the attributes inside class files.
+</body>
diff --git a/src/proguard/classfile/editor/CodeAttrInfoEditor.java b/src/proguard/classfile/editor/CodeAttrInfoEditor.java
new file mode 100644
index 0000000..c882cb9
--- /dev/null
+++ b/src/proguard/classfile/editor/CodeAttrInfoEditor.java
@@ -0,0 +1,667 @@
+/* $Id: CodeAttrInfoEditor.java,v 1.9 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This AttrInfoVisitor accumulates specified changes to code, and then applies
+ * these accumulated changes to the code attributes that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class CodeAttrInfoEditor
+  implements AttrInfoVisitor,
+             InstructionVisitor,
+             ExceptionInfoVisitor,
+             LineNumberInfoVisitor,
+             LocalVariableInfoVisitor,
+             LocalVariableTypeInfoVisitor
+{
+    private int              codeLength;
+    private boolean          modified;
+    /*private*/public Instruction[]    preInsertions;
+    /*private*/public Instruction[]    postInsertions;
+    private boolean[]        deleted;
+
+    private int[]            instructionOffsetMap;
+
+    private StackSizeUpdater stackSizeUpdater;
+
+
+    /**
+     * Creates a new CodeAttrInfoEditor.
+     * @param codeLength an estimate of the maximum length of all the code that
+     *                   will be edited.
+     */
+    public CodeAttrInfoEditor(int codeLength)
+    {
+        this.codeLength = codeLength;
+
+        preInsertions    = new Instruction[codeLength];
+        postInsertions   = new Instruction[codeLength];
+        deleted          = new boolean[codeLength];
+
+        stackSizeUpdater = new StackSizeUpdater(codeLength);
+    }
+
+
+    /**
+     * Resets the accumulated code changes.
+     * @param codeLength the length of the code that will be edited next.
+     */
+    public void reset(int codeLength)
+    {
+        this.codeLength = codeLength;
+
+        // Try to reuse the previous arrays.
+        if (preInsertions.length < codeLength)
+        {
+            preInsertions  = new Instruction[codeLength];
+            postInsertions = new Instruction[codeLength];
+            deleted        = new boolean[codeLength];
+        }
+        else
+        {
+            for (int index = 0; index < codeLength; index++)
+            {
+                preInsertions[index]  = null;
+                postInsertions[index] = null;
+                deleted[index]        = false;
+            }
+        }
+
+        modified = false;
+    }
+
+
+    /**
+     * Remembers to replace the instruction at the given offset by the given
+     * instruction.
+     * @param instructionOffset the offset of the instruction to be replaced.
+     * @param instruction       the new instruction.
+     */
+    public void replaceInstruction(int instructionOffset, Instruction instruction)
+    {
+        deleteInstruction(instructionOffset);
+        insertBeforeInstruction(instructionOffset, instruction);
+    }
+
+
+    /**
+     * Remembers to replace the instruction at the given offset by the given
+     * instruction.
+     * @param instructionOffset the offset of the instruction to be replaced.
+     * @param instruction       the new instruction.
+     */
+    public void replaceInstruction2(int instructionOffset, Instruction instruction)
+    {
+        deleteInstruction(instructionOffset);
+        insertAfterInstruction(instructionOffset, instruction);
+    }
+
+
+    /**
+     * Remembers to place the given instruction right before the instruction
+     * at the given offset.
+     * @param instructionOffset the offset of the instruction.
+     * @param instruction       the new instruction.
+     */
+    public void insertBeforeInstruction(int instructionOffset, Instruction instruction)
+    {
+        if (instructionOffset < 0 ||
+            instructionOffset >= codeLength)
+        {
+            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
+        }
+
+        preInsertions[instructionOffset] = instruction;
+
+        modified = true;
+    }
+
+
+    /**
+     * Remembers to place the given instruction right after the instruction
+     * at the given offset.
+     * @param instructionOffset the offset of the instruction.
+     * @param instruction       the new instruction.
+     */
+    public void insertAfterInstruction(int instructionOffset, Instruction instruction)
+    {
+        if (instructionOffset < 0 ||
+            instructionOffset >= codeLength)
+        {
+            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
+        }
+
+        postInsertions[instructionOffset] = instruction;
+
+        modified = true;
+    }
+
+
+    /**
+     * Remembers to delete the instruction at the given offset.
+     * @param instructionOffset the offset of the instruction to be deleted.
+     */
+    public void deleteInstruction(int instructionOffset)
+    {
+        if (instructionOffset < 0 ||
+            instructionOffset >= codeLength)
+        {
+            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
+        }
+
+        deleted[instructionOffset] = true;
+
+        modified = true;
+    }
+
+
+    /**
+     * Returns whether the instruction at the given offset has been modified
+     * in any way.
+     */
+    public boolean isModified(int instructionOffset)
+    {
+        return preInsertions[instructionOffset]  != null ||
+               postInsertions[instructionOffset] != null ||
+               deleted[instructionOffset];
+
+    }
+
+
+    /**
+     * Returns whether any instruction has been modified in any way.
+     */
+    public boolean isModified()
+    {
+        return modified;
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
+
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        // Avoid doing any work if nothing is changing anyway.
+        if (!modified)
+        {
+            return;
+        }
+
+        // Move and remap the instructions.
+        codeAttrInfo.u4codeLength =
+            moveInstructions(classFile, methodInfo, codeAttrInfo);
+
+        // Remap the exception table.
+        codeAttrInfo.exceptionsAccept(classFile, methodInfo, this);
+
+        // Remap  the line number table and the local variable table.
+        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
+
+        // Remove exceptions with empty code blocks.
+        codeAttrInfo.u2exceptionTableLength =
+             removeEmptyExceptions(codeAttrInfo.exceptionTable,
+                                   codeAttrInfo.u2exceptionTableLength);
+
+        // Update maximum stack size.
+        stackSizeUpdater.visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
+    }
+
+
+    // Implementations for LineNumberInfoVisitor.
+
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo)
+    {
+        // Remap all line number table entries.
+        lineNumberTableAttrInfo.lineNumbersAccept(classFile, methodInfo, codeAttrInfo, this);
+
+        // Remove line numbers with empty code blocks.
+        lineNumberTableAttrInfo.u2lineNumberTableLength =
+           removeEmptyLineNumbers(lineNumberTableAttrInfo.lineNumberTable,
+                                  lineNumberTableAttrInfo.u2lineNumberTableLength,
+                                  codeAttrInfo.u4codeLength);
+    }
+
+
+    // Implementations for LocalVariableInfoVisitor.
+
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
+    {
+        // Remap all local variable table entries.
+        localVariableTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+
+        // Remove local variables with empty code blocks.
+        localVariableTableAttrInfo.u2localVariableTableLength =
+            removeEmptyLocalVariables(localVariableTableAttrInfo.localVariableTable,
+                                      localVariableTableAttrInfo.u2localVariableTableLength);
+    }
+
+
+    // Implementations for LocalVariableInfoVisitor.
+
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
+    {
+        // Remap all local variable table entries.
+        localVariableTypeTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+
+        // Remove local variables with empty code blocks.
+        localVariableTypeTableAttrInfo.u2localVariableTypeTableLength =
+            removeEmptyLocalVariableTypes(localVariableTypeTableAttrInfo.localVariableTypeTable,
+                                          localVariableTypeTableAttrInfo.u2localVariableTypeTableLength);
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction) {}
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
+
+
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+    {
+        // Adjust the branch offset.
+        branchInstruction.branchOffset = remapBranchOffset(offset,
+                                                           branchInstruction.branchOffset);
+    }
+
+
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
+    {
+        // Adjust the default jump offset.
+        tableSwitchInstruction.defaultOffset = remapBranchOffset(offset,
+                                                                 tableSwitchInstruction.defaultOffset);
+
+        // Adjust the jump offsets.
+        remapJumpOffsets(offset,
+                         tableSwitchInstruction.jumpOffsets,
+                         tableSwitchInstruction.highCase -
+                         tableSwitchInstruction.lowCase + 1);
+    }
+
+
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
+    {
+        // Adjust the default jump offset.
+        lookUpSwitchInstruction.defaultOffset = remapBranchOffset(offset,
+                                                                  lookUpSwitchInstruction.defaultOffset);
+
+        // Adjust the jump offsets.
+        remapJumpOffsets(offset,
+                         lookUpSwitchInstruction.jumpOffsets,
+                         lookUpSwitchInstruction.jumpOffsetCount);
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo)
+    {
+        // Remap the code offsets. Note that the instruction offset map also has
+        // an entry for the first offset after the code, for u2endpc.
+        exceptionInfo.u2startpc   = remapInstructionOffset(exceptionInfo.u2startpc);
+        exceptionInfo.u2endpc     = remapInstructionOffset(exceptionInfo.u2endpc);
+        exceptionInfo.u2handlerpc = remapInstructionOffset(exceptionInfo.u2handlerpc);
+    }
+
+
+    // Implementations for LineNumberInfoVisitor.
+
+    public void visitLineNumberInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberInfo lineNumberInfo)
+    {
+        // Remap the code offset.
+        lineNumberInfo.u2startpc = remapInstructionOffset(lineNumberInfo.u2startpc);
+    }
+
+
+    // Implementations for LocalVariableInfoVisitor.
+
+    public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo)
+    {
+        // Remap the code offset and length.
+        localVariableInfo.u2length  = remapBranchOffset(localVariableInfo.u2startpc,
+                                                        localVariableInfo.u2length);
+        localVariableInfo.u2startpc = remapInstructionOffset(localVariableInfo.u2startpc);
+    }
+
+
+    // Implementations for LocalVariableTypeInfoVisitor.
+
+    public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo)
+    {
+        // Remap the code offset and length.
+        localVariableTypeInfo.u2length  = remapBranchOffset(localVariableTypeInfo.u2startpc,
+                                                            localVariableTypeInfo.u2length);
+        localVariableTypeInfo.u2startpc = remapInstructionOffset(localVariableTypeInfo.u2startpc);
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Modifies the given code based on the previously specified changes.
+     *
+     * @param classFile    the class file of the code to be changed.
+     * @param methodInfo   the method of the code to be changed.
+     * @param codeAttrInfo the code to be changed.
+     * @return the new code length.
+     */
+    private int moveInstructions(ClassFile    classFile,
+                                 MethodInfo   methodInfo,
+                                 CodeAttrInfo codeAttrInfo)
+    {
+        byte[] oldCode   = codeAttrInfo.code;
+        int    oldLength = codeAttrInfo.u4codeLength;
+
+        // Make sure there is a sufficiently large instruction offset map.
+        if (instructionOffsetMap == null ||
+            instructionOffsetMap.length < oldLength + 1)
+        {
+            instructionOffsetMap = new int[oldLength + 1];
+        }
+
+        // Fill out the offset map that specifies the new instruction offsets,
+        // given their current instruction offsets, by going over the
+        // instructions, deleting and inserting instructions as specified.
+        int     oldOffset       = 0;
+        int     newOffset       = 0;
+        boolean lengthIncreased = false;
+        do
+        {
+            // Get the next instruction.
+            Instruction instruction = InstructionFactory.create(oldCode, oldOffset);
+
+            // Compute the mapping of the instruction.
+            newOffset = mapInstruction(instruction, oldOffset, newOffset);
+
+            oldOffset += instruction.length(oldOffset);
+
+            // Is the new instruction exceeding the available space?
+            if (newOffset > oldOffset)
+            {
+                // Remember to create a new code array later on.
+                lengthIncreased = true;
+            }
+        }
+        while (oldOffset < oldLength);
+
+        // Also add an entry for the first offset after the code.
+        instructionOffsetMap[oldOffset] = newOffset;
+
+        // Create a new code array if necessary.
+        if (lengthIncreased)
+        {
+            codeAttrInfo.code = new byte[newOffset];
+        }
+
+        // Now actually move the instructions based on this map.
+        oldOffset = 0;
+        do
+        {
+            // Get the next instruction.
+            Instruction instruction = InstructionFactory.create(oldCode, oldOffset);
+
+            // Move the instruction to its new offset.
+            moveInstruction(classFile, methodInfo, codeAttrInfo, oldOffset, instruction);
+
+            oldOffset += instruction.length(oldOffset);
+        }
+        while (oldOffset < oldLength);
+
+        return newOffset;
+    }
+
+
+    /**
+     * Fills out the instruction offset map for the given instruction with its
+     * new offset.
+     * @param instruction the instruction to be moved.
+     * @param oldOffset   the instruction's old offset.
+     * @param newOffset   the instruction's new offset.
+     * @return            the next new offset.
+     */
+    private int mapInstruction(Instruction instruction,
+                               int         oldOffset,
+                               int         newOffset)
+    {
+        instructionOffsetMap[oldOffset] = newOffset;
+
+        // Account for the pre-inserted instruction, if any.
+        Instruction preInstruction = preInsertions[oldOffset];
+        if (preInstruction != null)
+        {
+            newOffset += preInstruction.length(newOffset);
+        }
+
+        // Account for the current instruction, if it shouldn't be deleted.
+        if (!deleted[oldOffset] )
+        {
+            // Note that the instruction's length may change at its new offset,
+            // e.g. if it is a switch instruction.
+            newOffset += instruction.length(newOffset);
+        }
+
+        // Account for the post-inserted instruction, if any.
+        Instruction postInstruction = postInsertions[oldOffset];
+        if (postInstruction != null)
+        {
+            newOffset += postInstruction.length(newOffset);
+        }
+
+        return newOffset;
+    }
+
+
+    /**
+     * Moves the given instruction to its new offset.
+     */
+    private void moveInstruction(ClassFile    classFile,
+                                 MethodInfo   methodInfo,
+                                 CodeAttrInfo codeAttrInfo,
+                                 int          oldOffset,
+                                 Instruction  instruction)
+    {
+        int newOffset = remapInstructionOffset(oldOffset);
+
+        // Remap and insert the pre-inserted instruction, if any.
+        Instruction preInstruction = preInsertions[oldOffset];
+        if (preInstruction != null)
+        {
+            preInstruction.accept(classFile, methodInfo, codeAttrInfo, oldOffset, this);
+
+            preInstruction.write(codeAttrInfo, newOffset);
+
+            newOffset += preInstruction.length(newOffset);
+        }
+
+        // Remap and insert the current instruction, if it shouldn't be deleted.
+        if (!deleted[oldOffset])
+        {
+            instruction.accept(classFile, methodInfo, codeAttrInfo, oldOffset, this);
+
+            instruction.write(codeAttrInfo, newOffset);
+
+            newOffset += instruction.length(newOffset);
+        }
+
+        // Remap and insert the post-inserted instruction, if any.
+        Instruction postInstruction = postInsertions[oldOffset];
+        if (postInstruction != null)
+        {
+            postInstruction.accept(classFile, methodInfo, codeAttrInfo, oldOffset, this);
+
+            postInstruction.write(codeAttrInfo, newOffset);
+        }
+    }
+
+
+    /**
+     * Adjusts the given jump offsets for the instruction at the given offset.
+     */
+    private void remapJumpOffsets(int offset, int[] jumpOffsets, int length)
+    {
+        for (int index = 0; index < length; index++)
+        {
+            jumpOffsets[index] = remapBranchOffset(offset, jumpOffsets[index]);
+        }
+    }
+
+
+    /**
+     * Computes the new branch offset for the instruction at the given offset
+     * with the given branch offset.
+     */
+    private int remapBranchOffset(int offset, int branchOffset)
+    {
+        return remapInstructionOffset(offset + branchOffset) -
+               remapInstructionOffset(offset);
+    }
+
+
+    /**
+     * Computes the new instruction offset for the instruction at the given offset.
+     */
+    private int remapInstructionOffset(int offset)
+    {
+        if (offset < 0 ||
+            offset > codeLength)
+        {
+            throw new IllegalArgumentException("Invalid instruction offset ["+offset+"] in code with length ["+codeLength+"]");
+        }
+
+        return instructionOffsetMap[offset];
+    }
+
+
+    /**
+     * Returns the given list of exceptions, without the ones that have empty
+     * code blocks.
+     */
+    private int removeEmptyExceptions(ExceptionInfo[] exceptionInfos,
+                                      int             exceptionInfoCount)
+    {
+        // Overwrite all empty exceptions.
+        int newIndex = 0;
+        for (int index = 0; index < exceptionInfoCount; index++)
+        {
+            ExceptionInfo exceptionInfo = exceptionInfos[index];
+            if (exceptionInfo.u2startpc < exceptionInfo.u2endpc)
+            {
+                exceptionInfos[newIndex++] = exceptionInfo;
+            }
+        }
+
+        return newIndex;
+    }
+
+
+    /**
+     * Returns the given list of line numbers, without the ones that have empty
+     * code blocks.
+     */
+    private int removeEmptyLineNumbers(LineNumberInfo[] lineNumberInfos,
+                                       int              lineNumberInfoCount,
+                                       int              codeLength)
+    {
+        // Overwrite all empty localVariables.
+        int newIndex = 0;
+        for (int index = 0; index < lineNumberInfoCount; index++)
+        {
+            LineNumberInfo lineNumberInfo = lineNumberInfos[index];
+            int startpc = lineNumberInfo.u2startpc;
+            if (               startpc < codeLength &&
+                (index == 0 || startpc > lineNumberInfos[index-1].u2startpc))
+            {
+                lineNumberInfos[newIndex++] = lineNumberInfo;
+            }
+        }
+
+        return newIndex;
+    }
+
+
+    /**
+     * Returns the given list of local variables, without the ones that have empty
+     * code blocks.
+     */
+    private int removeEmptyLocalVariables(LocalVariableInfo[] localVariableInfos,
+                                          int                 localVariableInfoCount)
+    {
+        // Overwrite all empty exceptions.
+        int newIndex = 0;
+        for (int index = 0; index < localVariableInfoCount; index++)
+        {
+            LocalVariableInfo localVariableInfo = localVariableInfos[index];
+            if (localVariableInfo.u2length > 0)
+            {
+                localVariableInfos[newIndex++] = localVariableInfo;
+            }
+        }
+
+        return newIndex;
+    }
+
+
+    /**
+     * Returns the given list of local variable types, without the ones that
+     * have empty code blocks.
+     */
+    private int removeEmptyLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos,
+                                              int                     localVariableTypeInfoCount)
+    {
+        // Overwrite all empty exceptions.
+        int newIndex = 0;
+        for (int index = 0; index < localVariableTypeInfoCount; index++)
+        {
+            LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index];
+            if (localVariableTypeInfo.u2length > 0)
+            {
+                localVariableTypeInfos[newIndex++] = localVariableTypeInfo;
+            }
+        }
+
+        return newIndex;
+    }
+}
diff --git a/src/proguard/classfile/editor/CodeAttrInfoEditorResetter.java b/src/proguard/classfile/editor/CodeAttrInfoEditorResetter.java
new file mode 100644
index 0000000..6e0b678
--- /dev/null
+++ b/src/proguard/classfile/editor/CodeAttrInfoEditorResetter.java
@@ -0,0 +1,76 @@
+/* $Id: CodeAttrInfoEditorResetter.java,v 1.3 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This AttrInfoVisitor resets it CodeAttrInfoEditor whenever it visits a
+ * code attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class CodeAttrInfoEditorResetter implements AttrInfoVisitor
+{
+    private CodeAttrInfoEditor codeAttrInfoEditor;
+
+
+    /**
+     * Creates a new CodeAttrInfoEditorResetter.
+     * @param codeAttrInfoEditor the code attribute editor that will be reset.
+     */
+    public CodeAttrInfoEditorResetter(CodeAttrInfoEditor codeAttrInfoEditor)
+    {
+        this.codeAttrInfoEditor = codeAttrInfoEditor;
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
+
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        codeAttrInfoEditor.reset(codeAttrInfo.u4codeLength);
+    }
+}
diff --git a/src/proguard/classfile/editor/ComparableCpInfo.java b/src/proguard/classfile/editor/ComparableCpInfo.java
new file mode 100644
index 0000000..3b7c539
--- /dev/null
+++ b/src/proguard/classfile/editor/ComparableCpInfo.java
@@ -0,0 +1,177 @@
+/* $Id: ComparableCpInfo.java,v 1.4 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This class is a <code>Comparable</code> wrapper of <code>CpInfo</code>
+ * objects. It can store an index, in order to identify the constant pool
+ * entry after it has been sorted. The comparison is primarily based on the
+ * types of the constant pool entries, and secondarily on the contents of
+ * the constant pool entries.
+ *
+ * @author Eric Lafortune
+ */
+class ComparableCpInfo implements Comparable, CpInfoVisitor
+{
+    private static int[] PRIORITIES = new int[13];
+    static
+    {
+        PRIORITIES[ClassConstants.CONSTANT_Class]              = 0;
+        PRIORITIES[ClassConstants.CONSTANT_Fieldref]           = 1;
+        PRIORITIES[ClassConstants.CONSTANT_Methodref]          = 2;
+        PRIORITIES[ClassConstants.CONSTANT_InterfaceMethodref] = 3;
+        PRIORITIES[ClassConstants.CONSTANT_String]             = 4;
+        PRIORITIES[ClassConstants.CONSTANT_Integer]            = 5;
+        PRIORITIES[ClassConstants.CONSTANT_Float]              = 6;
+        PRIORITIES[ClassConstants.CONSTANT_Long]               = 7;
+        PRIORITIES[ClassConstants.CONSTANT_Double]             = 8;
+        PRIORITIES[ClassConstants.CONSTANT_NameAndType]        = 9;
+        PRIORITIES[ClassConstants.CONSTANT_Utf8]               = 10;
+    }
+
+    private ClassFile classFile;
+    private int       thisIndex;
+    private CpInfo    thisCpInfo;
+    private CpInfo    otherCpInfo;
+    private int       result;
+
+
+    public ComparableCpInfo(ClassFile classFile, int index, CpInfo cpInfo)
+    {
+        this.classFile  = classFile;
+        this.thisIndex  = index;
+        this.thisCpInfo = cpInfo;
+    }
+
+
+    public int getIndex()
+    {
+        return thisIndex;
+    }
+
+
+    public CpInfo getCpInfo()
+    {
+        return thisCpInfo;
+    }
+
+
+    // Implementations for Comparable.
+
+    public int compareTo(Object other)
+    {
+        ComparableCpInfo otherComparableCpInfo = (ComparableCpInfo)other;
+
+        otherCpInfo = otherComparableCpInfo.thisCpInfo;
+
+        // Compare based on the original indices, if the actual constant pool
+        // entries are the same.
+        if (thisCpInfo == otherCpInfo)
+        {
+            int otherIndex = otherComparableCpInfo.thisIndex;
+
+            return thisIndex <  otherIndex ? -1 :
+                   thisIndex == otherIndex ?  0 :
+                                              1;
+        }
+
+        // Compare based on the tags, if they are different.
+        int thisTag  = thisCpInfo.getTag();
+        int otherTag = otherCpInfo.getTag();
+
+        if (thisTag != otherTag)
+        {
+            return PRIORITIES[thisTag] < PRIORITIES[otherTag] ? -1 : 1;
+        }
+
+        // Otherwise compare based on the contents of the CpInfo objects.
+        thisCpInfo.accept(classFile, this);
+
+        return result;
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo)
+    {
+        // In JDK 1.4, we can use Integer.compare(a,b).
+        result = new Integer(integerCpInfo.getValue()).compareTo(new Integer(((IntegerCpInfo)otherCpInfo).getValue()));
+    }
+
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo)
+    {
+        // In JDK 1.4, we can use Long.compare(a,b).
+        result = new Long(longCpInfo.getValue()).compareTo(new Long(((LongCpInfo)otherCpInfo).getValue()));
+    }
+
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo)
+    {
+        // In JDK 1.4, we can use Float.compare(a,b).
+        result = new Float(floatCpInfo.getValue()).compareTo(new Float(((FloatCpInfo)otherCpInfo).getValue()));
+    }
+
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo)
+    {
+        // In JDK 1.4, we can use Double.compare(a,b).
+        result = new Double(doubleCpInfo.getValue()).compareTo(new Double(((DoubleCpInfo)otherCpInfo).getValue()));
+    }
+
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
+    {
+        result = stringCpInfo.getString(classFile).compareTo(((StringCpInfo)otherCpInfo).getString(classFile));
+    }
+
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo)
+    {
+        result = utf8CpInfo.getString().compareTo(((Utf8CpInfo)otherCpInfo).getString());
+    }
+
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    {
+        result = fieldrefCpInfo.getName(classFile).compareTo(((FieldrefCpInfo)otherCpInfo).getName(classFile));
+    }
+
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    {
+        result = interfaceMethodrefCpInfo.getName(classFile).compareTo(((InterfaceMethodrefCpInfo)otherCpInfo).getName(classFile));
+    }
+
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    {
+        result = methodrefCpInfo.getName(classFile).compareTo(((MethodrefCpInfo)otherCpInfo).getName(classFile));
+    }
+
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    {
+        result = classCpInfo.getName(classFile).compareTo(((ClassCpInfo)otherCpInfo).getName(classFile));
+    }
+
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo)
+    {
+        result = nameAndTypeCpInfo.getName(classFile).compareTo(((NameAndTypeCpInfo)otherCpInfo).getName(classFile));
+    }
+}
diff --git a/src/proguard/classfile/editor/ConstantPoolEditor.java b/src/proguard/classfile/editor/ConstantPoolEditor.java
new file mode 100644
index 0000000..ec99ae2
--- /dev/null
+++ b/src/proguard/classfile/editor/ConstantPoolEditor.java
@@ -0,0 +1,531 @@
+/* $Id: ConstantPoolEditor.java,v 1.7 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.*;
+
+/**
+ * This class can add constant pool entries to given class files.
+ *
+ * @author Eric Lafortune
+ */
+public class ConstantPoolEditor
+{
+    /**
+     * Finds or creates a StringCpInfo constant pool entry with the given value,
+     * in the given class file.
+     * @return the constant pool index of the ClassCpInfo.
+     */
+    public int addStringCpInfo(ProgramClassFile programClassFile,
+                              String            string,
+                              ClassFile         referencedClassFile)
+    {
+        CpInfo[] constantPool      = programClassFile.constantPool;
+        int      constantPoolCount = programClassFile.u2constantPoolCount;
+
+        // Check if the entry already exists.
+        for (int index = 1; index < constantPoolCount; index++)
+        {
+            CpInfo cpInfo = constantPool[index];
+
+            if (cpInfo != null &&
+                cpInfo.getTag() == ClassConstants.CONSTANT_String)
+            {
+                StringCpInfo classCpInfo = (StringCpInfo)cpInfo;
+                if (classCpInfo.getString(programClassFile).equals(string))
+                {
+                    return index;
+                }
+            }
+        }
+
+        int nameIndex = addUtf8CpInfo(programClassFile, string);
+
+        return addCpInfo(programClassFile,
+                         new StringCpInfo(nameIndex,
+                                          referencedClassFile));
+    }
+
+
+    /**
+     * Finds or creates a FieldrefCpInfo constant pool entry with the given
+     * class name, field name, and descriptor, in the given class file.
+     * @return the constant pool index of the FieldrefCpInfo.
+     */
+    public int addFieldrefCpInfo(ProgramClassFile programClassFile,
+                                 String           className,
+                                 String           name,
+                                 String           descriptor,
+                                 ClassFile        referencedClassFile,
+                                 MemberInfo       referencedMemberInfo,
+                                 ClassFile[]      referencedClassFiles)
+    {
+        return addFieldrefCpInfo(programClassFile,
+                                 className,
+                                 addNameAndTypeCpInfo(programClassFile,
+                                                      name,
+                                                      descriptor,
+                                                      referencedClassFiles),
+                                 referencedClassFile,
+                                 referencedMemberInfo);
+    }
+
+
+    /**
+     * Finds or creates a FieldrefCpInfo constant pool entry with the given
+     * class name, field name, and descriptor, in the given class file.
+     * @return the constant pool index of the FieldrefCpInfo.
+     */
+    public int addFieldrefCpInfo(ProgramClassFile programClassFile,
+                                 String           className,
+                                 int              nameAndTypeIndex,
+                                 ClassFile        referencedClassFile,
+                                 MemberInfo       referencedMemberInfo)
+    {
+        return addFieldrefCpInfo(programClassFile,
+                                 addClassCpInfo(programClassFile,
+                                                className,
+                                                referencedClassFile),
+                                 nameAndTypeIndex,
+                                 referencedClassFile,
+                                 referencedMemberInfo);
+    }
+
+
+    /**
+     * Finds or creates a FieldrefCpInfo constant pool entry with the given
+     * class constant pool entry index, field name, and descriptor, in the
+     * given class file.
+     * @return the constant pool index of the FieldrefCpInfo.
+     */
+    public int addFieldrefCpInfo(ProgramClassFile programClassFile,
+                                 int              classIndex,
+                                 String           name,
+                                 String           descriptor,
+                                 ClassFile        referencedClassFile,
+                                 MemberInfo       referencedMemberInfo,
+                                 ClassFile[]      referencedClassFiles)
+    {
+        return addFieldrefCpInfo(programClassFile,
+                                 classIndex,
+                                 addNameAndTypeCpInfo(programClassFile,
+                                                      name,
+                                                      descriptor,
+                                                      referencedClassFiles),
+                                 referencedClassFile,
+                                 referencedMemberInfo);
+    }
+
+
+    /**
+     * Finds or creates a FieldrefCpInfo constant pool entry with the given
+     * class constant pool entry index and name and type constant pool entry index
+     * the given class file.
+     * @return the constant pool index of the FieldrefCpInfo.
+     */
+    public int addFieldrefCpInfo(ProgramClassFile programClassFile,
+                                 int              classIndex,
+                                 int              nameAndTypeIndex,
+                                 ClassFile        referencedClassFile,
+                                 MemberInfo       referencedMemberInfo)
+    {
+        CpInfo[] constantPool      = programClassFile.constantPool;
+        int      constantPoolCount = programClassFile.u2constantPoolCount;
+
+        // Check if the entry already exists.
+        for (int index = 1; index < constantPoolCount; index++)
+        {
+            CpInfo cpInfo = constantPool[index];
+
+            if (cpInfo != null &&
+                cpInfo.getTag() == ClassConstants.CONSTANT_Fieldref)
+            {
+                FieldrefCpInfo fieldrefCpInfo = (FieldrefCpInfo)cpInfo;
+                if (fieldrefCpInfo.u2classIndex       == classIndex &&
+                    fieldrefCpInfo.u2nameAndTypeIndex == nameAndTypeIndex)
+                {
+                    return index;
+                }
+            }
+        }
+
+        return addCpInfo(programClassFile,
+                         new FieldrefCpInfo(classIndex,
+                                            nameAndTypeIndex,
+                                            referencedClassFile,
+                                            referencedMemberInfo));
+    }
+
+
+    /**
+     * Finds or creates a MethodrefCpInfo constant pool entry with the given
+     * class name, method name, and descriptor, in the given class file.
+     * @return the constant pool index of the MethodrefCpInfo.
+     */
+    public int addMethodrefCpInfo(ProgramClassFile programClassFile,
+                                  String           className,
+                                  String           name,
+                                  String           descriptor,
+                                  ClassFile        referencedClassFile,
+                                  MemberInfo       referencedMemberInfo,
+                                  ClassFile[]      referencedClassFiles)
+    {
+        return addMethodrefCpInfo(programClassFile,
+                                  className,
+                                  addNameAndTypeCpInfo(programClassFile,
+                                                       name,
+                                                       descriptor,
+                                                       referencedClassFiles),
+                                  referencedClassFile,
+                                  referencedMemberInfo);
+    }
+
+
+    /**
+     * Finds or creates a MethodrefCpInfo constant pool entry with the given
+     * class name, method name, and descriptor, in the given class file.
+     * @return the constant pool index of the MethodrefCpInfo.
+     */
+    public int addMethodrefCpInfo(ProgramClassFile programClassFile,
+                                  String           className,
+                                  int              nameAndTypeIndex,
+                                  ClassFile        referencedClassFile,
+                                  MemberInfo       referencedMemberInfo)
+    {
+        return addMethodrefCpInfo(programClassFile,
+                                  addClassCpInfo(programClassFile,
+                                                 className,
+                                                 referencedClassFile),
+                                  nameAndTypeIndex,
+                                  referencedClassFile,
+                                  referencedMemberInfo);
+    }
+
+
+    /**
+     * Finds or creates a MethodrefCpInfo constant pool entry with the given
+     * class constant pool entry index, method name, and descriptor, in the
+     * given class file.
+     * @return the constant pool index of the MethodrefCpInfo.
+     */
+    public int addMethodrefCpInfo(ProgramClassFile programClassFile,
+                                  int              classIndex,
+                                  String           name,
+                                  String           descriptor,
+                                  ClassFile        referencedClassFile,
+                                  MemberInfo       referencedMemberInfo,
+                                  ClassFile[]      referencedClassFiles)
+    {
+        return addMethodrefCpInfo(programClassFile,
+                                  classIndex,
+                                  addNameAndTypeCpInfo(programClassFile,
+                                                       name,
+                                                       descriptor,
+                                                       referencedClassFiles),
+                                  referencedClassFile,
+                                  referencedMemberInfo);
+    }
+
+
+    /**
+     * Finds or creates a MethodrefCpInfo constant pool entry with the given
+     * class constant pool entry index and name and type constant pool entry index
+     * the given class file.
+     * @return the constant pool index of the MethodrefCpInfo.
+     */
+    public int addMethodrefCpInfo(ProgramClassFile programClassFile,
+                                  int              classIndex,
+                                  int              nameAndTypeIndex,
+                                  ClassFile        referencedClassFile,
+                                  MemberInfo       referencedMemberInfo)
+    {
+        CpInfo[] constantPool      = programClassFile.constantPool;
+        int      constantPoolCount = programClassFile.u2constantPoolCount;
+
+        // Check if the entry already exists.
+        for (int index = 1; index < constantPoolCount; index++)
+        {
+            CpInfo cpInfo = constantPool[index];
+
+            if (cpInfo != null &&
+                cpInfo.getTag() == ClassConstants.CONSTANT_Methodref)
+            {
+                MethodrefCpInfo methodrefCpInfo = (MethodrefCpInfo)cpInfo;
+                if (methodrefCpInfo.u2classIndex       == classIndex &&
+                    methodrefCpInfo.u2nameAndTypeIndex == nameAndTypeIndex)
+                {
+                    return index;
+                }
+            }
+        }
+
+        return addCpInfo(programClassFile,
+                         new MethodrefCpInfo(classIndex,
+                                             nameAndTypeIndex,
+                                             referencedClassFile,
+                                             referencedMemberInfo));
+    }
+
+
+    /**
+     * Finds or creates a InterfaceMethodrefCpInfo constant pool entry with the
+     * given class name, method name, and descriptor, in the given class file.
+     * @return the constant pool index of the InterfaceMethodrefCpInfo.
+     */
+    public int addInterfaceMethodrefCpInfo(ProgramClassFile programClassFile,
+                                           String           className,
+                                           String           name,
+                                           String           descriptor,
+                                           ClassFile        referencedClassFile,
+                                           MemberInfo       referencedMemberInfo,
+                                           ClassFile[]      referencedClassFiles)
+    {
+        return addInterfaceMethodrefCpInfo(programClassFile,
+                                           className,
+                                           addNameAndTypeCpInfo(programClassFile,
+                                                                name,
+                                                                descriptor,
+                                                                referencedClassFiles),
+                                                                referencedClassFile,
+                                                                referencedMemberInfo);
+    }
+
+
+    /**
+     * Finds or creates a InterfaceMethodrefCpInfo constant pool entry with the
+     * given class name, method name, and descriptor, in the given class file.
+     * @return the constant pool index of the InterfaceMethodrefCpInfo.
+     */
+    public int addInterfaceMethodrefCpInfo(ProgramClassFile programClassFile,
+                                           String           className,
+                                           int              nameAndTypeIndex,
+                                           ClassFile        referencedClassFile,
+                                           MemberInfo       referencedMemberInfo)
+    {
+        return addInterfaceMethodrefCpInfo(programClassFile,
+                                           addClassCpInfo(programClassFile,
+                                                          className,
+                                                          referencedClassFile),
+                                                          nameAndTypeIndex,
+                                                          referencedClassFile,
+                                                          referencedMemberInfo);
+    }
+
+
+    /**
+     * Finds or creates a InterfaceMethodrefCpInfo constant pool entry with the
+     * given class constant pool entry index, method name, and descriptor, in
+     * the given class file.
+     * @return the constant pool index of the InterfaceMethodrefCpInfo.
+     */
+    public int addInterfaceMethodrefCpInfo(ProgramClassFile programClassFile,
+                                           int              classIndex,
+                                           String           name,
+                                           String           descriptor,
+                                           ClassFile        referencedClassFile,
+                                           MemberInfo       referencedMemberInfo,
+                                           ClassFile[]      referencedClassFiles)
+    {
+        return addInterfaceMethodrefCpInfo(programClassFile,
+                                           classIndex,
+                                           addNameAndTypeCpInfo(programClassFile,
+                                                                name,
+                                                                descriptor,
+                                                                referencedClassFiles),
+                                                                referencedClassFile,
+                                                                referencedMemberInfo);
+    }
+
+
+    /**
+     * Finds or creates a InterfaceMethodrefCpInfo constant pool entry with the
+     * given class constant pool entry index and name and type constant pool
+     * entry index the given class file.
+     * @return the constant pool index of the InterfaceMethodrefCpInfo.
+     */
+    public int addInterfaceMethodrefCpInfo(ProgramClassFile programClassFile,
+                                           int              classIndex,
+                                           int              nameAndTypeIndex,
+                                           ClassFile        referencedClassFile,
+                                           MemberInfo       referencedMemberInfo)
+    {
+        CpInfo[] constantPool      = programClassFile.constantPool;
+        int      constantPoolCount = programClassFile.u2constantPoolCount;
+
+        // Check if the entry already exists.
+        for (int index = 1; index < constantPoolCount; index++)
+        {
+            CpInfo cpInfo = constantPool[index];
+
+            if (cpInfo != null &&
+                            cpInfo.getTag() == ClassConstants.CONSTANT_InterfaceMethodref)
+            {
+                InterfaceMethodrefCpInfo methodrefCpInfo = (InterfaceMethodrefCpInfo)cpInfo;
+                if (methodrefCpInfo.u2classIndex       == classIndex &&
+                                methodrefCpInfo.u2nameAndTypeIndex == nameAndTypeIndex)
+                {
+                    return index;
+                }
+            }
+        }
+
+        return addCpInfo(programClassFile,
+                         new InterfaceMethodrefCpInfo(classIndex,
+                                                      nameAndTypeIndex,
+                                                      referencedClassFile,
+                                                      referencedMemberInfo));
+    }
+
+
+    /**
+     * Finds or creates a ClassCpInfo constant pool entry with the given name,
+     * in the given class file.
+     * @return the constant pool index of the ClassCpInfo.
+     */
+    public int addClassCpInfo(ProgramClassFile programClassFile,
+                              String           name,
+                              ClassFile        referencedClassFile)
+    {
+        CpInfo[] constantPool        = programClassFile.constantPool;
+        int      constantPoolCount = programClassFile.u2constantPoolCount;
+
+        // Check if the entry already exists.
+        for (int index = 1; index < constantPoolCount; index++)
+        {
+            CpInfo cpInfo = constantPool[index];
+
+            if (cpInfo != null &&
+                cpInfo.getTag() == ClassConstants.CONSTANT_Class)
+            {
+                ClassCpInfo classCpInfo = (ClassCpInfo)cpInfo;
+                if (classCpInfo.getName(programClassFile).equals(name))
+                {
+                    return index;
+                }
+            }
+        }
+
+        int nameIndex = addUtf8CpInfo(programClassFile, name);
+
+        return addCpInfo(programClassFile,
+                         new ClassCpInfo(nameIndex,
+                                         referencedClassFile));
+    }
+
+
+    /**
+     * Finds or creates a NameAndTypeCpInfo constant pool entry with the given
+     * name and type, in the given class file.
+     * @return the constant pool index of the NameAndTypeCpInfo.
+     */
+    public int addNameAndTypeCpInfo(ProgramClassFile programClassFile,
+                                    String           name,
+                                    String           type,
+                                    ClassFile[]      referencedClassFiles)
+    {
+        CpInfo[] constantPool        = programClassFile.constantPool;
+        int      constantPoolCount = programClassFile.u2constantPoolCount;
+
+        // Check if the entry already exists.
+        for (int index = 1; index < constantPoolCount; index++)
+        {
+            CpInfo cpInfo = constantPool[index];
+
+            if (cpInfo != null &&
+                cpInfo.getTag() == ClassConstants.CONSTANT_NameAndType)
+            {
+                NameAndTypeCpInfo nameAndTypeCpInfo = (NameAndTypeCpInfo)cpInfo;
+                if (nameAndTypeCpInfo.getName(programClassFile).equals(name) &&
+                    nameAndTypeCpInfo.getType(programClassFile).equals(type))
+                {
+                    return index;
+                }
+            }
+        }
+
+        int nameIndex       = addUtf8CpInfo(programClassFile, name);
+        int descriptorIndex = addUtf8CpInfo(programClassFile, type);
+
+        return addCpInfo(programClassFile,
+                         new NameAndTypeCpInfo(nameIndex,
+                                               descriptorIndex,
+                                               referencedClassFiles));
+    }
+
+
+    /**
+     * Finds or creates an Utf8CpInfo constant pool entry for the given string,
+     * in the given class file.
+     * @return the constant pool index of the Utf8CpInfo.
+     */
+    public int addUtf8CpInfo(ProgramClassFile programClassFile,
+                             String           string)
+    {
+        CpInfo[] constantPool        = programClassFile.constantPool;
+        int      constantPoolCount = programClassFile.u2constantPoolCount;
+
+        // Check if the entry already exists.
+        for (int index = 1; index < constantPoolCount; index++)
+        {
+            CpInfo cpInfo = constantPool[index];
+
+            if (cpInfo != null &&
+                cpInfo.getTag() == ClassConstants.CONSTANT_Utf8)
+            {
+                Utf8CpInfo utf8CpInfo = (Utf8CpInfo)cpInfo;
+                if (utf8CpInfo.getString().equals(string))
+                {
+                    return index;
+                }
+            }
+        }
+
+        return addCpInfo(programClassFile, new Utf8CpInfo(string));
+    }
+
+
+    /**
+     * Adds a given constant pool entry to the end of the constant pool
+     * in the given class file.
+     * @return the constant pool index for the added entry.
+     */
+    private int addCpInfo(ProgramClassFile programClassFile,
+                          CpInfo           cpInfo)
+    {
+        CpInfo[] constantPool        = programClassFile.constantPool;
+        int      constantPoolCount = programClassFile.u2constantPoolCount;
+
+        // Make sure there is enough space for another constant pool entry.
+        if (constantPoolCount == constantPool.length)
+        {
+            programClassFile.constantPool = new CpInfo[constantPoolCount+1];
+            System.arraycopy(constantPool, 0,
+                             programClassFile.constantPool, 0,
+                             constantPoolCount);
+            constantPool = programClassFile.constantPool;
+        }
+
+        // Create a new Utf8CpInfo for the given string.
+        constantPool[programClassFile.u2constantPoolCount++] = cpInfo;
+
+        return constantPoolCount;
+    }
+}
diff --git a/src/proguard/classfile/editor/ConstantPoolRemapper.java b/src/proguard/classfile/editor/ConstantPoolRemapper.java
new file mode 100644
index 0000000..ef7460f
--- /dev/null
+++ b/src/proguard/classfile/editor/ConstantPoolRemapper.java
@@ -0,0 +1,592 @@
+/* $Id: ConstantPoolRemapper.java,v 1.9 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This ClassFileVisitor remaps all possible references to constant pool entries
+ * of the classes that it visits, based on a given index map. It is assumed that
+ * the constant pool entries themselves have already been remapped.
+ *
+ * @author Eric Lafortune
+ */
+public class ConstantPoolRemapper
+  implements ClassFileVisitor,
+             CpInfoVisitor,
+             MemberInfoVisitor,
+             AttrInfoVisitor,
+             InstructionVisitor,
+             InnerClassesInfoVisitor,
+             ExceptionInfoVisitor,
+             LocalVariableInfoVisitor,
+             LocalVariableTypeInfoVisitor,
+             AnnotationVisitor,
+             ElementValueVisitor
+{
+    private CodeAttrInfoEditor codeAttrInfoEditor;
+
+    private int[] cpIndexMap;
+
+
+    /**
+     * Creates a new ConstantPoolRemapper.
+     * @param codeLength an estimate of the maximum length of all the code that
+     *                   will be edited.
+     */
+    public ConstantPoolRemapper(int codeLength)
+    {
+        codeAttrInfoEditor = new CodeAttrInfoEditor(codeLength);
+    }
+
+
+    /**
+     * Sets the given mapping of old constant pool entry indexes to their new
+     * indexes.
+     */
+    public void setCpIndexMap(int[] cpIndexMap)
+    {
+        this.cpIndexMap = cpIndexMap;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Remap the local constant pool references.
+        programClassFile.u2thisClass  = remapCpIndex(programClassFile.u2thisClass);
+        programClassFile.u2superClass = remapCpIndex(programClassFile.u2superClass);
+
+        remapCpIndexArray(programClassFile.u2interfaces,
+                          programClassFile.u2interfacesCount);
+
+        // Remap the references of the contant pool entries themselves.
+        programClassFile.constantPoolEntriesAccept(this);
+
+        // Remap the references in all fields, methods, and attributes.
+        programClassFile.fieldsAccept(this);
+        programClassFile.methodsAccept(this);
+        programClassFile.attributesAccept(this);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    {
+        classCpInfo.u2nameIndex =
+            remapCpIndex(classCpInfo.u2nameIndex);
+    }
+
+
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo)
+    {
+        // Nothing to do.
+    }
+
+
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    {
+        fieldrefCpInfo.u2classIndex =
+            remapCpIndex(fieldrefCpInfo.u2classIndex);
+        fieldrefCpInfo.u2nameAndTypeIndex =
+            remapCpIndex(fieldrefCpInfo.u2nameAndTypeIndex);
+    }
+
+
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo)
+    {
+        // Nothing to do.
+    }
+
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo)
+    {
+        // Nothing to do.
+    }
+
+
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    {
+        interfaceMethodrefCpInfo.u2classIndex =
+            remapCpIndex(interfaceMethodrefCpInfo.u2classIndex);
+        interfaceMethodrefCpInfo.u2nameAndTypeIndex =
+            remapCpIndex(interfaceMethodrefCpInfo.u2nameAndTypeIndex);
+    }
+
+
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo)
+    {
+        // Nothing to do.
+    }
+
+
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    {
+        methodrefCpInfo.u2classIndex =
+            remapCpIndex(methodrefCpInfo.u2classIndex);
+        methodrefCpInfo.u2nameAndTypeIndex =
+            remapCpIndex(methodrefCpInfo.u2nameAndTypeIndex);
+    }
+
+
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo)
+    {
+        nameAndTypeCpInfo.u2nameIndex =
+            remapCpIndex(nameAndTypeCpInfo.u2nameIndex);
+        nameAndTypeCpInfo.u2descriptorIndex =
+            remapCpIndex(nameAndTypeCpInfo.u2descriptorIndex);
+    }
+
+
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
+    {
+        stringCpInfo.u2stringIndex =
+            remapCpIndex(stringCpInfo.u2stringIndex);
+    }
+
+
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo)
+    {
+        // Nothing to do.
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        visitMemberInfo(programClassFile, programFieldInfo);
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        visitMemberInfo(programClassFile, programMethodInfo);
+    }
+
+
+    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    {
+        // Remap the local constant pool references.
+        programMemberInfo.u2nameIndex =
+            remapCpIndex(programMemberInfo.u2nameIndex);
+        programMemberInfo.u2descriptorIndex =
+            remapCpIndex(programMemberInfo.u2descriptorIndex);
+
+        // Remap the constant pool references of the remaining attributes.
+        programMemberInfo.attributesAccept(programClassFile, this);
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    {
+        // Library class files are left unchanged.
+    }
+
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        // Library class files are left unchanged.
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo)
+    {
+        unknownAttrInfo.u2attrNameIndex =
+            remapCpIndex(unknownAttrInfo.u2attrNameIndex);
+
+        // There's not much else we can do with unknown attributes.
+    }
+
+
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
+    {
+        innerClassesAttrInfo.u2attrNameIndex =
+            remapCpIndex(innerClassesAttrInfo.u2attrNameIndex);
+
+        // Remap the constant pool references of the inner classes.
+        innerClassesAttrInfo.innerClassEntriesAccept(classFile, this);
+    }
+
+
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
+    {
+        enclosingMethodAttrInfo.u2attrNameIndex =
+            remapCpIndex(enclosingMethodAttrInfo.u2attrNameIndex);
+        enclosingMethodAttrInfo.u2classIndex =
+            remapCpIndex(enclosingMethodAttrInfo.u2classIndex);
+        enclosingMethodAttrInfo.u2nameAndTypeIndex =
+            remapCpIndex(enclosingMethodAttrInfo.u2nameAndTypeIndex);
+    }
+
+
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo)
+    {
+        constantValueAttrInfo.u2attrNameIndex =
+            remapCpIndex(constantValueAttrInfo.u2attrNameIndex);
+        constantValueAttrInfo.u2constantValueIndex =
+            remapCpIndex(constantValueAttrInfo.u2constantValueIndex);
+    }
+
+
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo)
+    {
+        exceptionsAttrInfo.u2attrNameIndex =
+            remapCpIndex(exceptionsAttrInfo.u2attrNameIndex);
+
+        // Remap the constant pool references of the exceptions.
+        remapCpIndexArray(exceptionsAttrInfo.u2exceptionIndexTable,
+                          exceptionsAttrInfo.u2numberOfExceptions);
+    }
+
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        codeAttrInfo.u2attrNameIndex =
+            remapCpIndex(codeAttrInfo.u2attrNameIndex);
+
+        // Initially, the code attribute editor doesn't contain any changes.
+        codeAttrInfoEditor.reset(codeAttrInfo.u4codeLength);
+
+        // Remap the constant pool references of the instructions.
+        codeAttrInfo.instructionsAccept(classFile, methodInfo, this);
+
+        // Apply the code atribute editor. It will only contain any changes if
+        // the code length is changing at any point.
+        codeAttrInfoEditor.visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
+
+        // Remap the constant pool references of the exceptions and attributes.
+        codeAttrInfo.exceptionsAccept(classFile, methodInfo, this);
+        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
+    }
+
+
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo)
+    {
+        lineNumberTableAttrInfo.u2attrNameIndex =
+            remapCpIndex(lineNumberTableAttrInfo.u2attrNameIndex);
+    }
+
+
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
+    {
+        localVariableTableAttrInfo.u2attrNameIndex =
+            remapCpIndex(localVariableTableAttrInfo.u2attrNameIndex);
+
+        // Remap the constant pool references of the local variables.
+        localVariableTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+    }
+
+
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
+    {
+        localVariableTypeTableAttrInfo.u2attrNameIndex =
+            remapCpIndex(localVariableTypeTableAttrInfo.u2attrNameIndex);
+
+        // Remap the constant pool references of the local variables.
+        localVariableTypeTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+    }
+
+
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo)
+    {
+        sourceFileAttrInfo.u2attrNameIndex =
+            remapCpIndex(sourceFileAttrInfo.u2attrNameIndex);
+        sourceFileAttrInfo.u2sourceFileIndex =
+            remapCpIndex(sourceFileAttrInfo.u2sourceFileIndex);
+    }
+
+
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo)
+    {
+        sourceDirAttrInfo.u2attrNameIndex =
+            remapCpIndex(sourceDirAttrInfo.u2attrNameIndex);
+        sourceDirAttrInfo.u2sourceDirIndex =
+            remapCpIndex(sourceDirAttrInfo.u2sourceDirIndex);
+    }
+
+
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo)
+    {
+        deprecatedAttrInfo.u2attrNameIndex =
+            remapCpIndex(deprecatedAttrInfo.u2attrNameIndex);
+    }
+
+
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo)
+    {
+        syntheticAttrInfo.u2attrNameIndex =
+            remapCpIndex(syntheticAttrInfo.u2attrNameIndex);
+    }
+
+
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
+    {
+        signatureAttrInfo.u2attrNameIndex =
+            remapCpIndex(signatureAttrInfo.u2attrNameIndex);
+        signatureAttrInfo.u2signatureIndex =
+            remapCpIndex(signatureAttrInfo.u2signatureIndex);
+    }
+
+
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
+    {
+        runtimeVisibleAnnotationsAttrInfo.u2attrNameIndex =
+            remapCpIndex(runtimeVisibleAnnotationsAttrInfo.u2attrNameIndex);
+
+        // Remap the constant pool references of the annotations.
+        runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
+    {
+        runtimeInvisibleAnnotationsAttrInfo.u2attrNameIndex =
+            remapCpIndex(runtimeInvisibleAnnotationsAttrInfo.u2attrNameIndex);
+
+        // Remap the constant pool references of the annotations.
+        runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
+    {
+        runtimeVisibleParameterAnnotationsAttrInfo.u2attrNameIndex =
+            remapCpIndex(runtimeVisibleParameterAnnotationsAttrInfo.u2attrNameIndex);
+
+        // Remap the constant pool references of the annotations.
+        runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
+    {
+        runtimeInvisibleParameterAnnotationsAttrInfo.u2attrNameIndex =
+            remapCpIndex(runtimeInvisibleParameterAnnotationsAttrInfo.u2attrNameIndex);
+
+        // Remap the constant pool references of the annotations.
+        runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
+    {
+        annotationDefaultAttrInfo.u2attrNameIndex =
+            remapCpIndex(annotationDefaultAttrInfo.u2attrNameIndex);
+
+        // Remap the constant pool references of the annotations.
+        annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
+    }
+
+
+    // Implementations for InnerClassesInfoVisitor.
+
+    public void visitInnerClassesInfo(ClassFile classFile, InnerClassesInfo innerClassesInfo)
+    {
+        if (innerClassesInfo.u2innerClassInfoIndex != 0)
+        {
+            innerClassesInfo.u2innerClassInfoIndex =
+                remapCpIndex(innerClassesInfo.u2innerClassInfoIndex);
+        }
+
+        if (innerClassesInfo.u2outerClassInfoIndex != 0)
+        {
+            innerClassesInfo.u2outerClassInfoIndex =
+                remapCpIndex(innerClassesInfo.u2outerClassInfoIndex);
+        }
+
+        if (innerClassesInfo.u2innerNameIndex != 0)
+        {
+            innerClassesInfo.u2innerNameIndex =
+                remapCpIndex(innerClassesInfo.u2innerNameIndex);
+        }
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo)
+    {
+        if (exceptionInfo.u2catchType != 0)
+        {
+            exceptionInfo.u2catchType =
+                remapCpIndex(exceptionInfo.u2catchType);
+        }
+    }
+
+
+    // Implementations for LocalVariableInfoVisitor.
+
+    public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo)
+    {
+        localVariableInfo.u2nameIndex =
+            remapCpIndex(localVariableInfo.u2nameIndex);
+        localVariableInfo.u2descriptorIndex =
+            remapCpIndex(localVariableInfo.u2descriptorIndex);
+    }
+
+
+    // Implementations for LocalVariableTypeInfoVisitor.
+
+    public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo)
+    {
+        localVariableTypeInfo.u2nameIndex =
+            remapCpIndex(localVariableTypeInfo.u2nameIndex);
+        localVariableTypeInfo.u2signatureIndex =
+            remapCpIndex(localVariableTypeInfo.u2signatureIndex);
+    }
+
+
+    // Implementations for AnnotationVisitor.
+
+    public void visitAnnotation(ClassFile classFile, Annotation annotation)
+    {
+        annotation.u2typeIndex =
+            remapCpIndex(annotation.u2typeIndex);
+
+        // Remap the constant pool references of the element values.
+        annotation.elementValuesAccept(classFile, this);
+    }
+
+
+    // Implementations for ElementValueVisitor.
+
+    public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue)
+    {
+        constantElementValue.u2elementName =
+            remapCpIndex(constantElementValue.u2elementName);
+        constantElementValue.u2constantValueIndex =
+            remapCpIndex(constantElementValue.u2constantValueIndex);
+    }
+
+
+    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+    {
+        enumConstantElementValue.u2elementName =
+            remapCpIndex(enumConstantElementValue.u2elementName);
+        enumConstantElementValue.u2typeNameIndex =
+            remapCpIndex(enumConstantElementValue.u2typeNameIndex);
+        enumConstantElementValue.u2constantNameIndex =
+            remapCpIndex(enumConstantElementValue.u2constantNameIndex);
+    }
+
+
+    public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
+    {
+        classElementValue.u2elementName =
+            remapCpIndex(classElementValue.u2elementName);
+        classElementValue.u2classInfoIndex =
+            remapCpIndex(classElementValue.u2classInfoIndex);
+    }
+
+
+    public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue)
+    {
+        annotationElementValue.u2elementName =
+            remapCpIndex(annotationElementValue.u2elementName);
+
+        // Remap the constant pool references of the annotation.
+        annotationElementValue.annotationAccept(classFile, this);
+    }
+
+
+    public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
+    {
+        arrayElementValue.u2elementName =
+            remapCpIndex(arrayElementValue.u2elementName);
+
+        // Remap the constant pool references of the element values.
+        arrayElementValue.elementValuesAccept(classFile, annotation, this);
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+
+
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    {
+        // Remap the instruction, and get the old and the new instruction length.
+        int oldLength = cpInstruction.length(offset);
+
+        cpInstruction.cpIndex = remapCpIndex(cpInstruction.cpIndex);
+        cpInstruction.shrink();
+
+        int newLength = cpInstruction.length(offset);
+
+        // Is the code length changing?
+        if (newLength != oldLength ||
+            codeAttrInfoEditor.isModified())
+        {
+            // We have to go through the code attribute editor.
+            cpInstruction = new CpInstruction().copy(cpInstruction);
+
+            codeAttrInfoEditor.replaceInstruction(offset, cpInstruction);
+        }
+        else
+        {
+            // We can write the instruction directly.
+            cpInstruction.write(codeAttrInfo, offset);
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Remaps all constant pool indices in the given array.
+     */
+    private void remapCpIndexArray(int[] array, int length)
+    {
+        for (int index = 0; index < length; index++)
+        {
+            array[index] = remapCpIndex(array[index]);
+        }
+    }
+
+
+    /**
+     * Returns the new constant pool index of the entry at the
+     * given index.
+     */
+    private int remapCpIndex(int cpIndex)
+    {
+        return cpIndexMap[cpIndex];
+    }
+}
diff --git a/src/proguard/classfile/editor/ConstantPoolSorter.java b/src/proguard/classfile/editor/ConstantPoolSorter.java
new file mode 100644
index 0000000..9291b5b
--- /dev/null
+++ b/src/proguard/classfile/editor/ConstantPoolSorter.java
@@ -0,0 +1,128 @@
+/* $Id: ConstantPoolSorter.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import java.util.Arrays;
+
+import proguard.classfile.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This ClassFileVisitor sorts the constant pool entries of the classes that
+ * it visits. The sorting is based on the types of the constant pool entries
+ * in the first place, and on their contents in the second place.
+ *
+ * @author Eric Lafortune
+ */
+public class ConstantPoolSorter implements ClassFileVisitor
+{
+    private int[]                cpIndexMap;
+    private ComparableCpInfo[]   comparableConstantPool;
+    private ConstantPoolRemapper constantPoolRemapper;
+
+
+    /**
+     * Creates a new ConstantPoolSorter.
+     * @param codeLength an estimate of the maximum length of all the code that
+     *                   will be edited.
+     */
+    public ConstantPoolSorter(int codeLength)
+    {
+        constantPoolRemapper = new ConstantPoolRemapper(codeLength);
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Sort the constant pool and set up an index map.
+        sortConstantPool(programClassFile,
+                         programClassFile.constantPool,
+                         programClassFile.u2constantPoolCount);
+
+        // Remap all constant pool references.
+        constantPoolRemapper.setCpIndexMap(cpIndexMap);
+        constantPoolRemapper.visitProgramClassFile(programClassFile);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Sorts the given constant pool.
+     */
+    private void sortConstantPool(ClassFile classFile, CpInfo[] constantPool, int length)
+    {
+        if (cpIndexMap == null ||
+            cpIndexMap.length < length)
+        {
+            cpIndexMap             = new int[length];
+            comparableConstantPool = new ComparableCpInfo[length];
+        }
+
+        // Initialize an array whose elements can be compared.
+        for (int oldIndex = 1; oldIndex < length; oldIndex++)
+        {
+            CpInfo cpInfo = constantPool[oldIndex];
+
+            // Long entries take up two entries, the second of which is null.
+            if (cpInfo == null)
+            {
+                cpInfo = constantPool[oldIndex-1];
+            }
+
+            comparableConstantPool[oldIndex] = new ComparableCpInfo(classFile,
+                                                                    oldIndex,
+                                                                    cpInfo);
+        }
+
+        // Sort the array.
+        Arrays.sort(comparableConstantPool, 1, length);
+
+        // Save the sorted elements.
+        CpInfo previousCpInfo = null;
+        for (int newIndex = 1; newIndex < length; newIndex++)
+        {
+            ComparableCpInfo comparableCpInfo = comparableConstantPool[newIndex];
+
+            // Fill out the map array.
+            int oldIndex = comparableCpInfo.getIndex();
+            cpIndexMap[oldIndex] = newIndex;
+
+            // Copy the sorted constant pool entry over to the constant pool.
+            // Long entries take up two entries, the second of which is null.
+            CpInfo cpInfo = comparableCpInfo.getCpInfo();
+            constantPool[newIndex] = cpInfo != previousCpInfo ?
+                cpInfo :
+                null;
+
+            previousCpInfo = cpInfo;
+        }
+    }
+}
diff --git a/src/proguard/classfile/editor/StackSizeUpdater.java b/src/proguard/classfile/editor/StackSizeUpdater.java
new file mode 100644
index 0000000..08cd042
--- /dev/null
+++ b/src/proguard/classfile/editor/StackSizeUpdater.java
@@ -0,0 +1,357 @@
+/* $Id: StackSizeUpdater.java,v 1.12 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This AttrInfoVisitor computes and updates the maximum stack size of the
+ * code attributes that it visits.
+ *
+ * @author Eric Lafortune
+ */
+class      StackSizeUpdater
+implements AttrInfoVisitor,
+           InstructionVisitor,
+           ExceptionInfoVisitor
+{
+    //*
+    private static final boolean DEBUG = false;
+    /*/
+    private static boolean DEBUG       = true;
+    //*/
+
+
+    private boolean[] evaluated;
+
+    private boolean exitInstructionBlock;
+
+    private int stackSize;
+    private int maxStackSize;
+
+
+    /**
+     * Creates a new StackSizeUpdater.
+     * @param codeLength an estimate of the maximum length of all the code that
+     *                   will be visited.
+     */
+    public StackSizeUpdater(int codeLength)
+    {
+        evaluated = new boolean[codeLength];
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
+
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+//        DEBUG =
+//            classFile.getName().equals("abc/Def") &&
+//            methodInfo.getName(classFile).equals("abc");
+
+        // Try to reuse the previous array.
+        int codeLength = codeAttrInfo.u4codeLength;
+        if (evaluated.length < codeLength)
+        {
+            evaluated = new boolean[codeLength];
+        }
+        else
+        {
+            for (int index = 0; index < codeLength; index++)
+            {
+                evaluated[index] = false;
+            }
+        }
+
+        // The initial stack is always empty.
+        stackSize    = 0;
+        maxStackSize = 0;
+
+        // Evaluate the instruction block starting at the entry point of the method.
+        evaluateInstructionBlock(classFile, methodInfo, codeAttrInfo, 0);
+
+        // Evaluate the exception handlers.
+        codeAttrInfo.exceptionsAccept(classFile, methodInfo, this);
+
+        // Update the maximum stack size.
+        codeAttrInfo.u2maxStack = maxStackSize;
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
+    {
+        byte opcode = simpleInstruction.opcode;
+
+        // Some simple instructions exit from the current instruction block.
+        exitInstructionBlock =
+            opcode == InstructionConstants.OP_IRETURN ||
+            opcode == InstructionConstants.OP_LRETURN ||
+            opcode == InstructionConstants.OP_FRETURN ||
+            opcode == InstructionConstants.OP_DRETURN ||
+            opcode == InstructionConstants.OP_ARETURN ||
+            opcode == InstructionConstants.OP_RETURN  ||
+            opcode == InstructionConstants.OP_ATHROW;
+    }
+
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    {
+        // Constant pool instructions never end the current instruction block.
+        exitInstructionBlock = false;
+    }
+
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    {
+        byte opcode = variableInstruction.opcode;
+
+        // The ret instruction end the current instruction block.
+        exitInstructionBlock =
+            opcode == InstructionConstants.OP_RET;
+    }
+
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+    {
+        byte opcode = branchInstruction.opcode;
+
+        // Evaluate the target instruction blocks.
+        evaluateInstructionBlock(classFile,
+                                 methodInfo,
+                                 codeAttrInfo,
+                                 offset +
+                                 branchInstruction.branchOffset);
+
+        // Evaluate the instructions after a subroutine branch.
+        if (opcode == InstructionConstants.OP_JSR ||
+            opcode == InstructionConstants.OP_JSR_W)
+        {
+            // We assume subroutine calls (jsr and jsr_w instructions) don't
+            // change the stack, other than popping the return value.
+            stackSize -= 1;
+
+            evaluateInstructionBlock(classFile,
+                                     methodInfo,
+                                     codeAttrInfo,
+                                     offset + branchInstruction.length(offset));
+        }
+
+        // Some branch instructions always end the current instruction block.
+        exitInstructionBlock =
+            opcode == InstructionConstants.OP_GOTO   ||
+            opcode == InstructionConstants.OP_GOTO_W ||
+            opcode == InstructionConstants.OP_JSR    ||
+            opcode == InstructionConstants.OP_JSR_W;
+    }
+
+
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
+    {
+        // Evaluate the target instruction blocks.
+        evaluateInstructionBlocks(classFile,
+                                  methodInfo,
+                                  codeAttrInfo,
+                                  offset,
+                                  tableSwitchInstruction.jumpOffsets,
+                                  tableSwitchInstruction.jumpOffsetCount,
+                                  tableSwitchInstruction.defaultOffset);
+
+        // The switch instruction always ends the current instruction block.
+        exitInstructionBlock = true;
+    }
+
+
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
+    {
+        // Evaluate the target instruction blocks.
+        evaluateInstructionBlocks(classFile,
+                                  methodInfo,
+                                  codeAttrInfo,
+                                  offset,
+                                  lookUpSwitchInstruction.jumpOffsets,
+                                  lookUpSwitchInstruction.jumpOffsetCount,
+                                  lookUpSwitchInstruction.defaultOffset);
+
+        // The switch instruction always ends the current instruction block.
+        exitInstructionBlock = true;
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo)
+    {
+        // The stack size when entering the exception handler is always 1.
+        stackSize = 1;
+
+        // Evaluate the instruction block starting at the entry point of the
+        // exception handler.
+        evaluateInstructionBlock(classFile,
+                                 methodInfo,
+                                 codeAttrInfo,
+                                 exceptionInfo.u2handlerpc);
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Evaluates the blocks of instructions starting at the given jump offsets.
+     */
+    private void evaluateInstructionBlocks(ClassFile    classFile,
+                                           MethodInfo   methodInfo,
+                                           CodeAttrInfo codeAttrInfo,
+                                           int          instructionOffset,
+                                           int[]        jumpOffsets,
+                                           int          jumpOffsetCount,
+                                           int          defaultOffset)
+    {
+        // The jump offset arrays may be reused, so we have to make a local copy.
+        int[] jumpOffsetsCopy = new int[jumpOffsetCount];
+        System.arraycopy(jumpOffsets, 0, jumpOffsetsCopy, 0,  jumpOffsetCount);
+        jumpOffsets = jumpOffsetsCopy;
+
+        // Loop over all jump offsets.
+        for (int index = 0; index < jumpOffsetCount; index++)
+        {
+            // Evaluate the jump instruction block.
+            evaluateInstructionBlock(classFile,
+                                     methodInfo,
+                                     codeAttrInfo,
+                                     instructionOffset + jumpOffsets[index]);
+        }
+
+        // Also evaluate the default instruction block.
+        evaluateInstructionBlock(classFile,
+                                 methodInfo,
+                                 codeAttrInfo,
+                                 instructionOffset + defaultOffset);
+    }
+
+
+    /**
+     * Evaluates a block of instructions that hasn't been handled before,
+     * starting at the given offset and ending a branch instruction, a return
+     * instruction, or a throw instruction. Branch instructions are handled
+     * recursively.
+     */
+    private void evaluateInstructionBlock(ClassFile    classFile,
+                                          MethodInfo   methodInfo,
+                                          CodeAttrInfo codeAttrInfo,
+                                          int          instructionOffset)
+    {
+        if (DEBUG)
+        {
+            System.out.println("--");
+        }
+
+        // Remember the initial stack size.
+        int initialStackSize = stackSize;
+
+        // Remember the maximum stack size.
+        if (maxStackSize < stackSize)
+        {
+            maxStackSize = stackSize;
+        }
+
+        // Evaluate any instructions that haven't been evaluated before.
+        while (!evaluated[instructionOffset])
+        {
+            // Mark the instruction as evaluated.
+            evaluated[instructionOffset] = true;
+
+            Instruction instruction = InstructionFactory.create(codeAttrInfo.code,
+                                                                instructionOffset);
+
+            if (DEBUG)
+            {
+                int stackPushCount = instruction.stackPushCount(classFile);
+                int stackPopCount  = instruction.stackPopCount(classFile);
+                System.out.println("["+instructionOffset+"]: "+
+                                   stackSize+" - "+
+                                   stackPopCount+" + "+
+                                   stackPushCount+" = "+
+                                   (stackSize+stackPushCount-stackPopCount)+": "+
+                                   instruction.toString());
+            }
+
+            // Compute the instruction's effect on the stack size.
+            stackSize += instruction.stackPushCount(classFile) -
+                         instruction.stackPopCount(classFile);
+
+            // Remember the maximum stack size.
+            if (maxStackSize < stackSize)
+            {
+                maxStackSize = stackSize;
+            }
+
+            // Remember the next instruction offset.
+            int nextInstructionOffset = instructionOffset +
+                                        instruction.length(instructionOffset);
+
+            // Visit the instruction, in order to handle branches.
+            instruction.accept(classFile, methodInfo, codeAttrInfo, instructionOffset, this);
+
+            // Stop evaluating after a branch.
+            if (exitInstructionBlock)
+            {
+                break;
+            }
+
+            // Continue with the next instruction.
+            instructionOffset = nextInstructionOffset;
+
+            if (DEBUG)
+            {
+                if (evaluated[instructionOffset])
+                {
+                    System.out.println("-- ("+instructionOffset+" already evaluated)");
+                }
+            }
+        }
+
+        // Restore the stack size for possible subsequent instruction blocks.
+        this.stackSize = initialStackSize;
+    }
+}
diff --git a/src/proguard/classfile/editor/package.html b/src/proguard/classfile/editor/package.html
new file mode 100644
index 0000000..d37f541
--- /dev/null
+++ b/src/proguard/classfile/editor/package.html
@@ -0,0 +1,3 @@
+<body>
+This package contains visitors to edit byte code.
+</body>
diff --git a/src/proguard/classfile/instruction/AllInstructionVisitor.java b/src/proguard/classfile/instruction/AllInstructionVisitor.java
new file mode 100644
index 0000000..2673a6b
--- /dev/null
+++ b/src/proguard/classfile/instruction/AllInstructionVisitor.java
@@ -0,0 +1,71 @@
+/* $Id: AllInstructionVisitor.java,v 1.5 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.instruction;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.AttrInfoVisitor;
+
+/**
+ * This AttrInfoVisitor lets a given InstructionVisitor visit all Instruction
+ * objects of the CodeAttrInfo objects it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class AllInstructionVisitor implements AttrInfoVisitor
+{
+    private InstructionVisitor instructionVisitor;
+
+
+    public AllInstructionVisitor(InstructionVisitor instructionVisitor)
+    {
+        this.instructionVisitor = instructionVisitor;
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
+
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        codeAttrInfo.instructionsAccept(classFile, methodInfo, instructionVisitor);
+    }
+}
diff --git a/src/proguard/classfile/instruction/BranchInstruction.java b/src/proguard/classfile/instruction/BranchInstruction.java
new file mode 100644
index 0000000..67d015d
--- /dev/null
+++ b/src/proguard/classfile/instruction/BranchInstruction.java
@@ -0,0 +1,133 @@
+/* $Id: BranchInstruction.java,v 1.14 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.instruction;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+
+/**
+ * This interface describes an instruction that branches to a given offset in
+ * the code.
+ *
+ * @author Eric Lafortune
+ */
+public class BranchInstruction extends Instruction
+{
+    public int branchOffset;
+
+
+    /**
+     * Creates an uninitialized BranchInstruction.
+     */
+    public BranchInstruction() {}
+
+
+    public BranchInstruction(byte opcode, int branchOffset)
+    {
+        this.opcode       = opcode;
+        this.branchOffset = branchOffset;
+    }
+
+
+    /**
+     * Copies the given instruction into this instruction.
+     * @param branchInstruction the instruction to be copied.
+     * @return this instruction.
+     */
+    public BranchInstruction copy(BranchInstruction branchInstruction)
+    {
+        this.opcode       = branchInstruction.opcode;
+        this.branchOffset = branchInstruction.branchOffset;
+
+        return this;
+    }
+
+
+    // Implementations for Instruction.
+
+    public Instruction shrink()
+    {
+        // Is this a wide branch that can be replaced by a normal branch?
+        if (branchOffset << 16 >> 16 == branchOffset)
+        {
+            if      (opcode == InstructionConstants.OP_GOTO_W)
+            {
+                opcode = InstructionConstants.OP_GOTO;
+            }
+            else if (opcode == InstructionConstants.OP_JSR_W)
+            {
+                opcode = InstructionConstants.OP_JSR;
+            }
+        }
+
+        return this;
+    }
+
+    protected void readInfo(byte[] code, int offset)
+    {
+        branchOffset = readSignedValue(code, offset, branchOffsetSize());
+    }
+
+
+    protected void writeInfo(byte[] code, int offset)
+    {
+        writeValue(code, offset, branchOffset, branchOffsetSize());
+    }
+
+
+    public int length(int offset)
+    {
+        return 1 + branchOffsetSize();
+    }
+
+
+    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, InstructionVisitor instructionVisitor)
+    {
+        instructionVisitor.visitBranchInstruction(classFile, methodInfo, codeAttrInfo, offset, this);
+    }
+
+
+    public String toString(int offset)
+    {
+        return "["+offset+"] "+getName()+" (offset="+branchOffset+", target="+(offset+branchOffset)+")";
+    }
+
+
+    // Implementations for Object.
+
+    public String toString()
+    {
+        return getName()+" (offset="+branchOffset+")";
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Computes the appropriate branch offset size for this instruction.
+     */
+    private int branchOffsetSize()
+    {
+        return opcode == InstructionConstants.OP_GOTO_W ||
+               opcode == InstructionConstants.OP_JSR_W  ? 4 :
+                                                          2;
+    }
+}
diff --git a/src/proguard/classfile/instruction/CpInstruction.java b/src/proguard/classfile/instruction/CpInstruction.java
new file mode 100644
index 0000000..f8caa07
--- /dev/null
+++ b/src/proguard/classfile/instruction/CpInstruction.java
@@ -0,0 +1,267 @@
+/* $Id: CpInstruction.java,v 1.19 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.instruction;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.util.ClassUtil;
+import proguard.classfile.visitor.CpInfoVisitor;
+
+/**
+ * This Instruction represents an instruction that refers to an entry in the
+ * constant pool.
+ *
+ * @author Eric Lafortune
+ */
+public class CpInstruction extends Instruction
+implements   CpInfoVisitor
+{
+    public int cpIndex;
+    public int constant;
+
+
+    // Fields acting as return parameters for the CpInfoVisitor methods.
+    private int parameterStackDelta;
+    private int typeStackDelta;
+
+
+    /**
+     * Creates an uninitialized CpInstruction.
+     */
+    public CpInstruction() {}
+
+
+    /**
+     * Creates a new CpInstruction with the given opcode and constant pool index.
+     */
+    public CpInstruction(byte opcode, int cpIndex)
+    {
+        this(opcode, cpIndex, 0);
+    }
+
+
+    /**
+     * Creates a new CpInstruction with the given opcode, constant pool index,
+     * and constant.
+     */
+    public CpInstruction(byte opcode, int cpIndex, int constant)
+    {
+        this.opcode   = opcode;
+        this.cpIndex  = cpIndex;
+        this.constant = constant;
+    }
+
+
+    /**
+     * Copies the given instruction into this instruction.
+     * @param cpInstruction the instruction to be copied.
+     * @return this instruction.
+     */
+    public CpInstruction copy(CpInstruction cpInstruction)
+    {
+        this.opcode   = cpInstruction.opcode;
+        this.cpIndex  = cpInstruction.cpIndex;
+        this.constant = cpInstruction.constant;
+
+        return this;
+    }
+
+
+    // Implementations for Instruction.
+
+    public Instruction shrink()
+    {
+        if      (opcode == InstructionConstants.OP_LDC &&
+                 cpIndex > 0xff)
+        {
+            opcode = InstructionConstants.OP_LDC_W;
+        }
+        else if (opcode == InstructionConstants.OP_LDC_W &&
+                 cpIndex <= 0xff)
+        {
+            opcode = InstructionConstants.OP_LDC;
+        }
+
+        return this;
+    }
+
+    protected void readInfo(byte[] code, int offset)
+    {
+        int cpIndexSize  = cpIndexSize();
+        int constantSize = constantSize();
+
+        cpIndex  = readValue(code, offset, cpIndexSize);  offset += cpIndexSize;
+        constant = readValue(code, offset, constantSize);
+    }
+
+
+    protected void writeInfo(byte[] code, int offset)
+    {
+        int cpIndexSize  = cpIndexSize();
+        int constantSize = constantSize();
+
+        writeValue(code, offset, cpIndex,  cpIndexSize);  offset += cpIndexSize;
+        writeValue(code, offset, constant, constantSize);
+    }
+
+
+    public int length(int offset)
+    {
+        return 1 + cpIndexSize() + constantSize();
+    }
+
+
+    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, InstructionVisitor instructionVisitor)
+    {
+        instructionVisitor.visitCpInstruction(classFile, methodInfo, codeAttrInfo, offset, this);
+    }
+
+
+    public String toString(int offset)
+    {
+        return "["+offset+"] "+getName()+" (cpIndex="+cpIndex+")";
+    }
+
+
+    public int stackPopCount(ClassFile classFile)
+    {
+        int stackPopCount = super.stackPopCount(classFile);
+
+        // Some special cases.
+        switch (opcode)
+        {
+            case InstructionConstants.OP_MULTIANEWARRAY:
+                // For each dimension, an integer size is popped from the stack.
+                stackPopCount += constant;
+                break;
+
+            case InstructionConstants.OP_PUTSTATIC:
+            case InstructionConstants.OP_PUTFIELD:
+                // The field value is be popped from the stack.
+                classFile.constantPoolEntryAccept(cpIndex, this);
+                stackPopCount += typeStackDelta;
+                break;
+
+            case InstructionConstants.OP_INVOKEVIRTUAL:
+            case InstructionConstants.OP_INVOKESPECIAL:
+            case InstructionConstants.OP_INVOKESTATIC:
+            case InstructionConstants.OP_INVOKEINTERFACE:
+                // The some parameters may be popped from the stack.
+                classFile.constantPoolEntryAccept(cpIndex, this);
+                stackPopCount += parameterStackDelta;
+                break;
+        }
+
+        return stackPopCount;
+    }
+
+
+    public int stackPushCount(ClassFile classFile)
+    {
+        int stackPushCount = super.stackPushCount(classFile);
+
+        // Some special cases.
+        switch (opcode)
+        {
+            case InstructionConstants.OP_GETSTATIC:
+            case InstructionConstants.OP_GETFIELD:
+            case InstructionConstants.OP_INVOKEVIRTUAL:
+            case InstructionConstants.OP_INVOKESPECIAL:
+            case InstructionConstants.OP_INVOKESTATIC:
+            case InstructionConstants.OP_INVOKEINTERFACE:
+                // The field value or a return value may be pushed onto the stack.
+                classFile.constantPoolEntryAccept(cpIndex, this);
+                stackPushCount += typeStackDelta;
+                break;
+        }
+
+        return stackPushCount;
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+
+
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    {
+        String type = fieldrefCpInfo.getType(classFile);
+
+        typeStackDelta = ClassUtil.internalTypeSize(ClassUtil.internalMethodReturnType(type));
+    }
+
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    {
+        visitRefCpInfo(classFile, interfaceMethodrefCpInfo);
+    }
+
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    {
+        visitRefCpInfo(classFile, methodrefCpInfo);
+    }
+
+    private void visitRefCpInfo(ClassFile classFile, RefCpInfo methodrefCpInfo)
+    {
+        String type = methodrefCpInfo.getType(classFile);
+
+        parameterStackDelta = ClassUtil.internalMethodParameterSize(type);
+        typeStackDelta      = ClassUtil.internalTypeSize(ClassUtil.internalMethodReturnType(type));
+    }
+
+
+    // Implementations for Object.
+
+    public String toString()
+    {
+        return getName()+" (cpIndex="+cpIndex+")";
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Computes the appropriate constant pool index size for this instruction.
+     */
+    private int cpIndexSize()
+    {
+        return opcode == InstructionConstants.OP_LDC ? 1 :
+                                                       2;
+    }
+
+
+    /**
+     * Computes the appropriate constant size for this instruction.
+     */
+    private int constantSize()
+    {
+        return opcode == InstructionConstants.OP_MULTIANEWARRAY  ? 1 :
+               opcode == InstructionConstants.OP_INVOKEINTERFACE ? 2 :
+                                                                   0;
+    }
+}
diff --git a/src/proguard/classfile/instruction/Instruction.java b/src/proguard/classfile/instruction/Instruction.java
new file mode 100644
index 0000000..e0f9b4f
--- /dev/null
+++ b/src/proguard/classfile/instruction/Instruction.java
@@ -0,0 +1,866 @@
+/* $Id: Instruction.java,v 1.22 2004/12/11 00:12:41 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.instruction;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+
+/**
+ * Base class for representing instructions.
+ *
+ * @author Eric Lafortune
+ */
+public abstract class Instruction
+{
+    // An array for marking Category 2 instructions.
+    private static final boolean[] IS_CATEGORY2 = new boolean[]
+    {
+        false, // nop
+        false, // aconst_null
+        false, // iconst_m1
+        false, // iconst_0
+        false, // iconst_1
+        false, // iconst_2
+        false, // iconst_3
+        false, // iconst_4
+        false, // iconst_5
+        true,  // lconst_0
+        true,  // lconst_1
+        false, // fconst_0
+        false, // fconst_1
+        false, // fconst_2
+        true,  // dconst_0
+        true,  // dconst_1
+        false, // bipush
+        false, // sipush
+        false, // ldc
+        false, // ldc_w
+        true,  // ldc2_w
+        false, // iload
+        true,  // lload
+        false, // fload
+        true,  // dload
+        false, // aload
+        false, // iload_0
+        false, // iload_1
+        false, // iload_2
+        false, // iload_3
+        true,  // lload_0
+        true,  // lload_1
+        true,  // lload_2
+        true,  // lload_3
+        false, // fload_0
+        false, // fload_1
+        false, // fload_2
+        false, // fload_3
+        true,  // dload_0
+        true,  // dload_1
+        true,  // dload_2
+        true,  // dload_3
+        false, // aload_0
+        false, // aload_1
+        false, // aload_2
+        false, // aload_3
+        false, // iaload
+        true,  // laload
+        false, // faload
+        true,  // daload
+        false, // aaload
+        false, // baload
+        false, // caload
+        false, // saload
+        false, // istore
+        true,  // lstore
+        false, // fstore
+        true,  // dstore
+        false, // astore
+        false, // istore_0
+        false, // istore_1
+        false, // istore_2
+        false, // istore_3
+        true,  // lstore_0
+        true,  // lstore_1
+        true,  // lstore_2
+        true,  // lstore_3
+        false, // fstore_0
+        false, // fstore_1
+        false, // fstore_2
+        false, // fstore_3
+        true,  // dstore_0
+        true,  // dstore_1
+        true,  // dstore_2
+        true,  // dstore_3
+        false, // astore_0
+        false, // astore_1
+        false, // astore_2
+        false, // astore_3
+        false, // iastore
+        true,  // lastore
+        false, // fastore
+        true,  // dastore
+        false, // aastore
+        false, // bastore
+        false, // castore
+        false, // sastore
+        false, // pop
+        true,  // pop2
+        false, // dup
+        false, // dup_x1
+        false, // dup_x2
+        true,  // dup2
+        true,  // dup2_x1
+        true,  // dup2_x2
+        false, // swap
+        false, // iadd
+        true,  // ladd
+        false, // fadd
+        true,  // dadd
+        false, // isub
+        true,  // lsub
+        false, // fsub
+        true,  // dsub
+        false, // imul
+        true,  // lmul
+        false, // fmul
+        true,  // dmul
+        false, // idiv
+        true,  // ldiv
+        false, // fdiv
+        true,  // ddiv
+        false, // irem
+        true,  // lrem
+        false, // frem
+        true,  // drem
+        false, // ineg
+        true,  // lneg
+        false, // fneg
+        true,  // dneg
+        false, // ishl
+        true,  // lshl
+        false, // ishr
+        true,  // lshr
+        false, // iushr
+        true,  // lushr
+        false, // iand
+        true,  // land
+        false, // ior
+        true,  // lor
+        false, // ixor
+        true,  // lxor
+        false, // iinc
+        false, // i2l
+        false, // i2f
+        false, // i2d
+        true,  // l2i
+        true,  // l2f
+        true,  // l2d
+        false, // f2i
+        false, // f2l
+        false, // f2d
+        true,  // d2i
+        true,  // d2l
+        true,  // d2f
+        false, // i2b
+        false, // i2c
+        false, // i2s
+        true,  // lcmp
+        false, // fcmpl
+        false, // fcmpg
+        true,  // dcmpl
+        true,  // dcmpg
+        false, // ifeq
+        false, // ifne
+        false, // iflt
+        false, // ifge
+        false, // ifgt
+        false, // ifle
+        false, // ificmpeq
+        false, // ificmpne
+        false, // ificmplt
+        false, // ificmpge
+        false, // ificmpgt
+        false, // ificmple
+        false, // ifacmpeq
+        false, // ifacmpne
+        false, // goto
+        false, // jsr
+        false, // ret
+        false, // tableswitch
+        false, // lookupswitch
+        false, // ireturn
+        true,  // lreturn
+        false, // freturn
+        true,  // dreturn
+        false, // areturn
+        false, // return
+        false, // getstatic
+        false, // putstatic
+        false, // getfield
+        false, // putfield
+        false, // invokevirtual
+        false, // invokespecial
+        false, // invokestatic
+        false, // invokeinterface
+        false, // unused
+        false, // new
+        false, // newarray
+        false, // anewarray
+        false, // arraylength
+        false, // athrow
+        false, // checkcast
+        false, // instanceof
+        false, // monitorenter
+        false, // monitorexit
+        false, // wide
+        false, // multianewarray
+        false, // ifnull
+        false, // ifnonnull
+        false, // goto_w
+        false, // jsr_w
+    };
+
+
+    // An array containing the fixed number of entries popped from the stack,
+    // for all instructions.
+    private static final int[] STACK_POP_COUNTS = new int[]
+    {
+        0, // nop
+        0, // aconst_null
+        0, // iconst_m1
+        0, // iconst_0
+        0, // iconst_1
+        0, // iconst_2
+        0, // iconst_3
+        0, // iconst_4
+        0, // iconst_5
+        0, // lconst_0
+        0, // lconst_1
+        0, // fconst_0
+        0, // fconst_1
+        0, // fconst_2
+        0, // dconst_0
+        0, // dconst_1
+        0, // bipush
+        0, // sipush
+        0, // ldc
+        0, // ldc_w
+        0, // ldc2_w
+        0, // iload
+        0, // lload
+        0, // fload
+        0, // dload
+        0, // aload
+        0, // iload_0
+        0, // iload_1
+        0, // iload_2
+        0, // iload_3
+        0, // lload_0
+        0, // lload_1
+        0, // lload_2
+        0, // lload_3
+        0, // fload_0
+        0, // fload_1
+        0, // fload_2
+        0, // fload_3
+        0, // dload_0
+        0, // dload_1
+        0, // dload_2
+        0, // dload_3
+        0, // aload_0
+        0, // aload_1
+        0, // aload_2
+        0, // aload_3
+        2, // iaload
+        2, // laload
+        2, // faload
+        2, // daload
+        2, // aaload
+        2, // baload
+        2, // caload
+        2, // saload
+        1, // istore
+        2, // lstore
+        1, // fstore
+        2, // dstore
+        1, // astore
+        1, // istore_0
+        1, // istore_1
+        1, // istore_2
+        1, // istore_3
+        2, // lstore_0
+        2, // lstore_1
+        2, // lstore_2
+        2, // lstore_3
+        1, // fstore_0
+        1, // fstore_1
+        1, // fstore_2
+        1, // fstore_3
+        2, // dstore_0
+        2, // dstore_1
+        2, // dstore_2
+        2, // dstore_3
+        1, // astore_0
+        1, // astore_1
+        1, // astore_2
+        1, // astore_3
+        3, // iastore
+        4, // lastore
+        3, // fastore
+        4, // dastore
+        3, // aastore
+        3, // bastore
+        3, // castore
+        3, // sastore
+        1, // pop
+        2, // pop2
+        0, // dup
+        0, // dup_x1
+        0, // dup_x2
+        0, // dup2
+        0, // dup2_x1
+        0, // dup2_x2
+        0, // swap
+        2, // iadd
+        4, // ladd
+        2, // fadd
+        4, // dadd
+        2, // isub
+        4, // lsub
+        2, // fsub
+        4, // dsub
+        2, // imul
+        4, // lmul
+        2, // fmul
+        4, // dmul
+        2, // idiv
+        4, // ldiv
+        2, // fdiv
+        4, // ddiv
+        2, // irem
+        4, // lrem
+        2, // frem
+        4, // drem
+        1, // ineg
+        2, // lneg
+        1, // fneg
+        2, // dneg
+        2, // ishl
+        3, // lshl
+        2, // ishr
+        3, // lshr
+        2, // iushr
+        3, // lushr
+        2, // iand
+        4, // land
+        2, // ior
+        4, // lor
+        2, // ixor
+        4, // lxor
+        0, // iinc
+        1, // i2l
+        1, // i2f
+        1, // i2d
+        2, // l2i
+        2, // l2f
+        2, // l2d
+        1, // f2i
+        1, // f2l
+        1, // f2d
+        2, // d2i
+        2, // d2l
+        2, // d2f
+        1, // i2b
+        1, // i2c
+        1, // i2s
+        4, // lcmp
+        2, // fcmpl
+        2, // fcmpg
+        4, // dcmpl
+        4, // dcmpg
+        1, // ifeq
+        1, // ifne
+        1, // iflt
+        1, // ifge
+        1, // ifgt
+        1, // ifle
+        2, // ificmpeq
+        2, // ificmpne
+        2, // ificmplt
+        2, // ificmpge
+        2, // ificmpgt
+        2, // ificmple
+        2, // ifacmpeq
+        2, // ifacmpne
+        0, // goto
+        0, // jsr
+        0, // ret
+        1, // tableswitch
+        1, // lookupswitch
+        1, // ireturn
+        2, // lreturn
+        1, // freturn
+        2, // dreturn
+        1, // areturn
+        0, // return
+        0, // getstatic
+        0, // putstatic
+        1, // getfield
+        1, // putfield
+        1, // invokevirtual
+        1, // invokespecial
+        0, // invokestatic
+        1, // invokeinterface
+        0, // unused
+        0, // new
+        1, // newarray
+        1, // anewarray
+        1, // arraylength
+        1, // athrow
+        1, // checkcast
+        1, // instanceof
+        1, // monitorenter
+        1, // monitorexit
+        0, // wide
+        0, // multianewarray
+        1, // ifnull
+        1, // ifnonnull
+        0, // goto_w
+        0, // jsr_w
+    };
+
+
+    // An array containing the fixed number of entries pushed onto the stack,
+    // for all instructions.
+    private static final int[] STACK_PUSH_COUNTS = new int[]
+    {
+        0, // nop
+        1, // aconst_null
+        1, // iconst_m1
+        1, // iconst_0
+        1, // iconst_1
+        1, // iconst_2
+        1, // iconst_3
+        1, // iconst_4
+        1, // iconst_5
+        2, // lconst_0
+        2, // lconst_1
+        1, // fconst_0
+        1, // fconst_1
+        1, // fconst_2
+        2, // dconst_0
+        2, // dconst_1
+        1, // bipush
+        1, // sipush
+        1, // ldc
+        1, // ldc_w
+        2, // ldc2_w
+        1, // iload
+        2, // lload
+        1, // fload
+        2, // dload
+        1, // aload
+        1, // iload_0
+        1, // iload_1
+        1, // iload_2
+        1, // iload_3
+        2, // lload_0
+        2, // lload_1
+        2, // lload_2
+        2, // lload_3
+        1, // fload_0
+        1, // fload_1
+        1, // fload_2
+        1, // fload_3
+        2, // dload_0
+        2, // dload_1
+        2, // dload_2
+        2, // dload_3
+        1, // aload_0
+        1, // aload_1
+        1, // aload_2
+        1, // aload_3
+        1, // iaload
+        2, // laload
+        1, // faload
+        2, // daload
+        1, // aaload
+        1, // baload
+        1, // caload
+        1, // saload
+        0, // istore
+        0, // lstore
+        0, // fstore
+        0, // dstore
+        0, // astore
+        0, // istore_0
+        0, // istore_1
+        0, // istore_2
+        0, // istore_3
+        0, // lstore_0
+        0, // lstore_1
+        0, // lstore_2
+        0, // lstore_3
+        0, // fstore_0
+        0, // fstore_1
+        0, // fstore_2
+        0, // fstore_3
+        0, // dstore_0
+        0, // dstore_1
+        0, // dstore_2
+        0, // dstore_3
+        0, // astore_0
+        0, // astore_1
+        0, // astore_2
+        0, // astore_3
+        0, // iastore
+        0, // lastore
+        0, // fastore
+        0, // dastore
+        0, // aastore
+        0, // bastore
+        0, // castore
+        0, // sastore
+        0, // pop
+        0, // pop2
+        1, // dup
+        1, // dup_x1
+        1, // dup_x2
+        2, // dup2
+        2, // dup2_x1
+        2, // dup2_x2
+        0, // swap
+        1, // iadd
+        2, // ladd
+        1, // fadd
+        2, // dadd
+        1, // isub
+        2, // lsub
+        1, // fsub
+        2, // dsub
+        1, // imul
+        2, // lmul
+        1, // fmul
+        2, // dmul
+        1, // idiv
+        2, // ldiv
+        1, // fdiv
+        2, // ddiv
+        1, // irem
+        2, // lrem
+        1, // frem
+        2, // drem
+        1, // ineg
+        2, // lneg
+        1, // fneg
+        2, // dneg
+        1, // ishl
+        2, // lshl
+        1, // ishr
+        2, // lshr
+        1, // iushr
+        2, // lushr
+        1, // iand
+        2, // land
+        1, // ior
+        2, // lor
+        1, // ixor
+        2, // lxor
+        0, // iinc
+        2, // i2l
+        1, // i2f
+        2, // i2d
+        1, // l2i
+        1, // l2f
+        2, // l2d
+        1, // f2i
+        2, // f2l
+        2, // f2d
+        1, // d2i
+        2, // d2l
+        1, // d2f
+        1, // i2b
+        1, // i2c
+        1, // i2s
+        1, // lcmp
+        1, // fcmpl
+        1, // fcmpg
+        1, // dcmpl
+        1, // dcmpg
+        0, // ifeq
+        0, // ifne
+        0, // iflt
+        0, // ifge
+        0, // ifgt
+        0, // ifle
+        0, // ificmpeq
+        0, // ificmpne
+        0, // ificmplt
+        0, // ificmpge
+        0, // ificmpgt
+        0, // ificmple
+        0, // ifacmpeq
+        0, // ifacmpne
+        0, // goto
+        1, // jsr
+        0, // ret
+        0, // tableswitch
+        0, // lookupswitch
+        0, // ireturn
+        0, // lreturn
+        0, // freturn
+        0, // dreturn
+        0, // areturn
+        0, // return
+        0, // getstatic
+        0, // putstatic
+        0, // getfield
+        0, // putfield
+        0, // invokevirtual
+        0, // invokespecial
+        0, // invokestatic
+        0, // invokeinterface
+        0, // unused
+        1, // new
+        1, // newarray
+        1, // anewarray
+        1, // arraylength
+        0, // athrow
+        1, // checkcast
+        1, // instanceof
+        0, // monitorenter
+        0, // monitorexit
+        0, // wide
+        1, // multianewarray
+        0, // ifnull
+        0, // ifnonnull
+        0, // goto_w
+        1, // jsr_w
+    };
+
+
+    public byte opcode;
+
+
+    /**
+     * Shrinks this instruction to its shortest possible form.
+     * @return this instruction.
+     */
+    public abstract Instruction shrink();
+
+
+
+    /**
+     * Writes the Instruction back to the data in the byte array.
+     */
+    public final void write(CodeAttrInfo codeAttrInfo, int offset)
+    {
+        byte[] code  = codeAttrInfo.code;
+
+        // Write the wide opcode, if necessary.
+        if (isWide())
+        {
+            code[offset++] = InstructionConstants.OP_WIDE;
+        }
+
+        // Write the opcode.
+        code[offset++] = opcode;
+
+        // Write any additional arguments.
+        writeInfo(code, offset);
+    }
+
+
+    /**
+     * Returns whether the instruction is wide, i.e. preceded by a wide opcode.
+     * With the current specifications, only variable instructions can be wide.
+     */
+    protected boolean isWide()
+    {
+        return false;
+    }
+
+
+    /**
+     * Reads the data following the instruction opcode.
+     */
+    protected abstract void readInfo(byte[] code, int offset);
+
+
+    /**
+     * Writes data following the instruction opcode.
+     */
+    protected abstract void writeInfo(byte[] code, int offset);
+
+
+    /**
+     * Returns the length in bytes of the instruction.
+     */
+    public abstract int length(int offset);
+
+
+    /**
+     * Accepts the given visitor.
+     */
+    public abstract void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, InstructionVisitor instructionVisitor);
+
+
+    /**
+     * Returns a description of the instruction, at the given offset.
+     */
+    public abstract String toString(int offset);
+
+
+    /**
+     * Returns the name of the instruction.
+     */
+    public String getName()
+    {
+        return InstructionConstants.NAMES[opcode & 0xff];
+    }
+
+
+    /**
+     * Returns whether the instruction is a Category 2 instruction. This means
+     * that it operates on long or double arguments.
+     */
+    public boolean isCategory2()
+    {
+        return IS_CATEGORY2[opcode & 0xff];
+    }
+
+
+    /**
+     * Returns the number of entries popped from the stack during the execution
+     * of the instruction.
+     */
+    public int stackPopCount(ClassFile classFile)
+    {
+        return STACK_POP_COUNTS[opcode & 0xff];
+    }
+
+
+    /**
+     * Returns the number of entries pushed onto the stack during the execution
+     * of the instruction.
+     */
+    public int stackPushCount(ClassFile classFile)
+    {
+        return STACK_PUSH_COUNTS[opcode & 0xff];
+    }
+
+
+    // Small utility methods.
+
+    protected static int readByte(byte[] code, int offset)
+    {
+        return code[offset] & 0xff;
+    }
+
+    protected static int readShort(byte[] code, int offset)
+    {
+        return ((code[offset++] & 0xff) << 8) |
+               ( code[offset  ] & 0xff      );
+    }
+
+    protected static int readInt(byte[] code, int offset)
+    {
+        return ( code[offset++]         << 24) |
+               ((code[offset++] & 0xff) << 16) |
+               ((code[offset++] & 0xff) <<  8) |
+               ( code[offset  ] & 0xff       );
+    }
+
+    protected static int readValue(byte[] code, int offset, int valueSize)
+    {
+        switch (valueSize)
+        {
+            case 0: return 0;
+            case 1: return readByte( code, offset);
+            case 2: return readShort(code, offset);
+            case 4: return readInt(  code, offset);
+            default: throw new IllegalArgumentException("Unsupported value size ["+valueSize+"]");
+        }
+    }
+
+    protected static int readSignedByte(byte[] code, int offset)
+    {
+        return code[offset];
+    }
+
+    protected static int readSignedShort(byte[] code, int offset)
+    {
+        return (code[offset++] <<   8) |
+               (code[offset  ] & 0xff);
+    }
+
+    protected static int readSignedValue(byte[] code, int offset, int valueSize)
+    {
+        switch (valueSize)
+        {
+            case 0: return 0;
+            case 1: return readSignedByte( code, offset);
+            case 2: return readSignedShort(code, offset);
+            case 4: return readInt(        code, offset);
+            default: throw new IllegalArgumentException("Unsupported value size ["+valueSize+"]");
+        }
+    }
+
+    protected static void writeByte(byte[] code, int offset, int value)
+    {
+        if (value > 0xff)
+        {
+            throw new IllegalArgumentException("Byte value larger than 0xff ["+value+"]");
+        }
+
+        code[offset] = (byte)value;
+    }
+
+    protected static void writeShort(byte[] code, int offset, int value)
+    {
+        if (value > 0xffff)
+        {
+            throw new IllegalArgumentException("Short value larger than 0xffff ["+value+"]");
+        }
+
+        code[offset++] = (byte)(value >> 8);
+        code[offset  ] = (byte)(value     );
+    }
+
+    protected static void writeInt(byte[] code, int offset, int value)
+    {
+        code[offset++] = (byte)(value >> 24);
+        code[offset++] = (byte)(value >> 16);
+        code[offset++] = (byte)(value >>  8);
+        code[offset  ] = (byte)(value      );
+    }
+
+    protected static void writeValue(byte[] code, int offset, int value, int valueSize)
+    {
+        switch (valueSize)
+        {
+            case 0:                                  break;
+            case 1: writeByte( code, offset, value); break;
+            case 2: writeShort(code, offset, value); break;
+            case 4: writeInt(  code, offset, value); break;
+            default: throw new IllegalArgumentException("Unsupported value size ["+valueSize+"]");
+        }
+    }
+}
diff --git a/src/proguard/classfile/instruction/InstructionConstants.java b/src/proguard/classfile/instruction/InstructionConstants.java
new file mode 100644
index 0000000..60e9fec
--- /dev/null
+++ b/src/proguard/classfile/instruction/InstructionConstants.java
@@ -0,0 +1,449 @@
+/* $Id: InstructionConstants.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.instruction;
+
+/**
+ * Representation of an instruction.
+ *
+ * @author Eric Lafortune
+ */
+public interface InstructionConstants
+{
+    public static final byte OP_NOP              = 0;
+    public static final byte OP_ACONST_NULL      = 1;
+    public static final byte OP_ICONST_M1        = 2;
+    public static final byte OP_ICONST_0         = 3;
+    public static final byte OP_ICONST_1         = 4;
+    public static final byte OP_ICONST_2         = 5;
+    public static final byte OP_ICONST_3         = 6;
+    public static final byte OP_ICONST_4         = 7;
+    public static final byte OP_ICONST_5         = 8;
+    public static final byte OP_LCONST_0         = 9;
+    public static final byte OP_LCONST_1         = 10;
+    public static final byte OP_FCONST_0         = 11;
+    public static final byte OP_FCONST_1         = 12;
+    public static final byte OP_FCONST_2         = 13;
+    public static final byte OP_DCONST_0         = 14;
+    public static final byte OP_DCONST_1         = 15;
+    public static final byte OP_BIPUSH           = 16;
+    public static final byte OP_SIPUSH           = 17;
+    public static final byte OP_LDC              = 18;
+    public static final byte OP_LDC_W            = 19;
+    public static final byte OP_LDC2_W           = 20;
+    public static final byte OP_ILOAD            = 21;
+    public static final byte OP_LLOAD            = 22;
+    public static final byte OP_FLOAD            = 23;
+    public static final byte OP_DLOAD            = 24;
+    public static final byte OP_ALOAD            = 25;
+    public static final byte OP_ILOAD_0          = 26;
+    public static final byte OP_ILOAD_1          = 27;
+    public static final byte OP_ILOAD_2          = 28;
+    public static final byte OP_ILOAD_3          = 29;
+    public static final byte OP_LLOAD_0          = 30;
+    public static final byte OP_LLOAD_1          = 31;
+    public static final byte OP_LLOAD_2          = 32;
+    public static final byte OP_LLOAD_3          = 33;
+    public static final byte OP_FLOAD_0          = 34;
+    public static final byte OP_FLOAD_1          = 35;
+    public static final byte OP_FLOAD_2          = 36;
+    public static final byte OP_FLOAD_3          = 37;
+    public static final byte OP_DLOAD_0          = 38;
+    public static final byte OP_DLOAD_1          = 39;
+    public static final byte OP_DLOAD_2          = 40;
+    public static final byte OP_DLOAD_3          = 41;
+    public static final byte OP_ALOAD_0          = 42;
+    public static final byte OP_ALOAD_1          = 43;
+    public static final byte OP_ALOAD_2          = 44;
+    public static final byte OP_ALOAD_3          = 45;
+    public static final byte OP_IALOAD           = 46;
+    public static final byte OP_LALOAD           = 47;
+    public static final byte OP_FALOAD           = 48;
+    public static final byte OP_DALOAD           = 49;
+    public static final byte OP_AALOAD           = 50;
+    public static final byte OP_BALOAD           = 51;
+    public static final byte OP_CALOAD           = 52;
+    public static final byte OP_SALOAD           = 53;
+    public static final byte OP_ISTORE           = 54;
+    public static final byte OP_LSTORE           = 55;
+    public static final byte OP_FSTORE           = 56;
+    public static final byte OP_DSTORE           = 57;
+    public static final byte OP_ASTORE           = 58;
+    public static final byte OP_ISTORE_0         = 59;
+    public static final byte OP_ISTORE_1         = 60;
+    public static final byte OP_ISTORE_2         = 61;
+    public static final byte OP_ISTORE_3         = 62;
+    public static final byte OP_LSTORE_0         = 63;
+    public static final byte OP_LSTORE_1         = 64;
+    public static final byte OP_LSTORE_2         = 65;
+    public static final byte OP_LSTORE_3         = 66;
+    public static final byte OP_FSTORE_0         = 67;
+    public static final byte OP_FSTORE_1         = 68;
+    public static final byte OP_FSTORE_2         = 69;
+    public static final byte OP_FSTORE_3         = 70;
+    public static final byte OP_DSTORE_0         = 71;
+    public static final byte OP_DSTORE_1         = 72;
+    public static final byte OP_DSTORE_2         = 73;
+    public static final byte OP_DSTORE_3         = 74;
+    public static final byte OP_ASTORE_0         = 75;
+    public static final byte OP_ASTORE_1         = 76;
+    public static final byte OP_ASTORE_2         = 77;
+    public static final byte OP_ASTORE_3         = 78;
+    public static final byte OP_IASTORE          = 79;
+    public static final byte OP_LASTORE          = 80;
+    public static final byte OP_FASTORE          = 81;
+    public static final byte OP_DASTORE          = 82;
+    public static final byte OP_AASTORE          = 83;
+    public static final byte OP_BASTORE          = 84;
+    public static final byte OP_CASTORE          = 85;
+    public static final byte OP_SASTORE          = 86;
+    public static final byte OP_POP              = 87;
+    public static final byte OP_POP2             = 88;
+    public static final byte OP_DUP              = 89;
+    public static final byte OP_DUP_X1           = 90;
+    public static final byte OP_DUP_X2           = 91;
+    public static final byte OP_DUP2             = 92;
+    public static final byte OP_DUP2_X1          = 93;
+    public static final byte OP_DUP2_X2          = 94;
+    public static final byte OP_SWAP             = 95;
+    public static final byte OP_IADD             = 96;
+    public static final byte OP_LADD             = 97;
+    public static final byte OP_FADD             = 98;
+    public static final byte OP_DADD             = 99;
+    public static final byte OP_ISUB             = 100;
+    public static final byte OP_LSUB             = 101;
+    public static final byte OP_FSUB             = 102;
+    public static final byte OP_DSUB             = 103;
+    public static final byte OP_IMUL             = 104;
+    public static final byte OP_LMUL             = 105;
+    public static final byte OP_FMUL             = 106;
+    public static final byte OP_DMUL             = 107;
+    public static final byte OP_IDIV             = 108;
+    public static final byte OP_LDIV             = 109;
+    public static final byte OP_FDIV             = 110;
+    public static final byte OP_DDIV             = 111;
+    public static final byte OP_IREM             = 112;
+    public static final byte OP_LREM             = 113;
+    public static final byte OP_FREM             = 114;
+    public static final byte OP_DREM             = 115;
+    public static final byte OP_INEG             = 116;
+    public static final byte OP_LNEG             = 117;
+    public static final byte OP_FNEG             = 118;
+    public static final byte OP_DNEG             = 119;
+    public static final byte OP_ISHL             = 120;
+    public static final byte OP_LSHL             = 121;
+    public static final byte OP_ISHR             = 122;
+    public static final byte OP_LSHR             = 123;
+    public static final byte OP_IUSHR            = 124;
+    public static final byte OP_LUSHR            = 125;
+    public static final byte OP_IAND             = 126;
+    public static final byte OP_LAND             = 127;
+    public static final byte OP_IOR              = -128;
+    public static final byte OP_LOR              = -127;
+    public static final byte OP_IXOR             = -126;
+    public static final byte OP_LXOR             = -125;
+    public static final byte OP_IINC             = -124;
+    public static final byte OP_I2L              = -123;
+    public static final byte OP_I2F              = -122;
+    public static final byte OP_I2D              = -121;
+    public static final byte OP_L2I              = -120;
+    public static final byte OP_L2F              = -119;
+    public static final byte OP_L2D              = -118;
+    public static final byte OP_F2I              = -117;
+    public static final byte OP_F2L              = -116;
+    public static final byte OP_F2D              = -115;
+    public static final byte OP_D2I              = -114;
+    public static final byte OP_D2L              = -113;
+    public static final byte OP_D2F              = -112;
+    public static final byte OP_I2B              = -111;
+    public static final byte OP_I2C              = -110;
+    public static final byte OP_I2S              = -109;
+    public static final byte OP_LCMP             = -108;
+    public static final byte OP_FCMPL            = -107;
+    public static final byte OP_FCMPG            = -106;
+    public static final byte OP_DCMPL            = -105;
+    public static final byte OP_DCMPG            = -104;
+    public static final byte OP_IFEQ             = -103;
+    public static final byte OP_IFNE             = -102;
+    public static final byte OP_IFLT             = -101;
+    public static final byte OP_IFGE             = -100;
+    public static final byte OP_IFGT             = -99;
+    public static final byte OP_IFLE             = -98;
+    public static final byte OP_IFICMPEQ         = -97;
+    public static final byte OP_IFICMPNE         = -96;
+    public static final byte OP_IFICMPLT         = -95;
+    public static final byte OP_IFICMPGE         = -94;
+    public static final byte OP_IFICMPGT         = -93;
+    public static final byte OP_IFICMPLE         = -92;
+    public static final byte OP_IFACMPEQ         = -91;
+    public static final byte OP_IFACMPNE         = -90;
+    public static final byte OP_GOTO             = -89;
+    public static final byte OP_JSR              = -88;
+    public static final byte OP_RET              = -87;
+    public static final byte OP_TABLESWITCH      = -86;
+    public static final byte OP_LOOKUPSWITCH     = -85;
+    public static final byte OP_IRETURN          = -84;
+    public static final byte OP_LRETURN          = -83;
+    public static final byte OP_FRETURN          = -82;
+    public static final byte OP_DRETURN          = -81;
+    public static final byte OP_ARETURN          = -80;
+    public static final byte OP_RETURN           = -79;
+    public static final byte OP_GETSTATIC        = -78;
+    public static final byte OP_PUTSTATIC        = -77;
+    public static final byte OP_GETFIELD         = -76;
+    public static final byte OP_PUTFIELD         = -75;
+    public static final byte OP_INVOKEVIRTUAL    = -74;
+    public static final byte OP_INVOKESPECIAL    = -73;
+    public static final byte OP_INVOKESTATIC     = -72;
+    public static final byte OP_INVOKEINTERFACE  = -71;
+//  public static final byte OP_UNUSED           = -70;
+    public static final byte OP_NEW              = -69;
+    public static final byte OP_NEWARRAY         = -68;
+    public static final byte OP_ANEWARRAY        = -67;
+    public static final byte OP_ARRAYLENGTH      = -66;
+    public static final byte OP_ATHROW           = -65;
+    public static final byte OP_CHECKCAST        = -64;
+    public static final byte OP_INSTANCEOF       = -63;
+    public static final byte OP_MONITORENTER     = -62;
+    public static final byte OP_MONITOREXIT      = -61;
+    public static final byte OP_WIDE             = -60;
+    public static final byte OP_MULTIANEWARRAY   = -59;
+    public static final byte OP_IFNULL           = -58;
+    public static final byte OP_IFNONNULL        = -57;
+    public static final byte OP_GOTO_W           = -56;
+    public static final byte OP_JSR_W            = -55;
+
+
+    public static final String[] NAMES =
+    {
+        "nop",
+        "aconst_null",
+        "iconst_m1",
+        "iconst_0",
+        "iconst_1",
+        "iconst_2",
+        "iconst_3",
+        "iconst_4",
+        "iconst_5",
+        "lconst_0",
+        "lconst_1",
+        "fconst_0",
+        "fconst_1",
+        "fconst_2",
+        "dconst_0",
+        "dconst_1",
+        "bipush",
+        "sipush",
+        "ldc",
+        "ldc_w",
+        "ldc2_w",
+        "iload",
+        "lload",
+        "fload",
+        "dload",
+        "aload",
+        "iload_0",
+        "iload_1",
+        "iload_2",
+        "iload_3",
+        "lload_0",
+        "lload_1",
+        "lload_2",
+        "lload_3",
+        "fload_0",
+        "fload_1",
+        "fload_2",
+        "fload_3",
+        "dload_0",
+        "dload_1",
+        "dload_2",
+        "dload_3",
+        "aload_0",
+        "aload_1",
+        "aload_2",
+        "aload_3",
+        "iaload",
+        "laload",
+        "faload",
+        "daload",
+        "aaload",
+        "baload",
+        "caload",
+        "saload",
+        "istore",
+        "lstore",
+        "fstore",
+        "dstore",
+        "astore",
+        "istore_0",
+        "istore_1",
+        "istore_2",
+        "istore_3",
+        "lstore_0",
+        "lstore_1",
+        "lstore_2",
+        "lstore_3",
+        "fstore_0",
+        "fstore_1",
+        "fstore_2",
+        "fstore_3",
+        "dstore_0",
+        "dstore_1",
+        "dstore_2",
+        "dstore_3",
+        "astore_0",
+        "astore_1",
+        "astore_2",
+        "astore_3",
+        "iastore",
+        "lastore",
+        "fastore",
+        "dastore",
+        "aastore",
+        "bastore",
+        "castore",
+        "sastore",
+        "pop",
+        "pop2",
+        "dup",
+        "dup_x1",
+        "dup_x2",
+        "dup2",
+        "dup2_x1",
+        "dup2_x2",
+        "swap",
+        "iadd",
+        "ladd",
+        "fadd",
+        "dadd",
+        "isub",
+        "lsub",
+        "fsub",
+        "dsub",
+        "imul",
+        "lmul",
+        "fmul",
+        "dmul",
+        "idiv",
+        "ldiv",
+        "fdiv",
+        "ddiv",
+        "irem",
+        "lrem",
+        "frem",
+        "drem",
+        "ineg",
+        "lneg",
+        "fneg",
+        "dneg",
+        "ishl",
+        "lshl",
+        "ishr",
+        "lshr",
+        "iushr",
+        "lushr",
+        "iand",
+        "land",
+        "ior",
+        "lor",
+        "ixor",
+        "lxor",
+        "iinc",
+        "i2l",
+        "i2f",
+        "i2d",
+        "l2i",
+        "l2f",
+        "l2d",
+        "f2i",
+        "f2l",
+        "f2d",
+        "d2i",
+        "d2l",
+        "d2f",
+        "i2b",
+        "i2c",
+        "i2s",
+        "lcmp",
+        "fcmpl",
+        "fcmpg",
+        "dcmpl",
+        "dcmpg",
+        "ifeq",
+        "ifne",
+        "iflt",
+        "ifge",
+        "ifgt",
+        "ifle",
+        "ificmpeq",
+        "ificmpne",
+        "ificmplt",
+        "ificmpge",
+        "ificmpgt",
+        "ificmple",
+        "ifacmpeq",
+        "ifacmpne",
+        "goto",
+        "jsr",
+        "ret",
+        "tableswitch",
+        "lookupswitch",
+        "ireturn",
+        "lreturn",
+        "freturn",
+        "dreturn",
+        "areturn",
+        "return",
+        "getstatic",
+        "putstatic",
+        "getfield",
+        "putfield",
+        "invokevirtual",
+        "invokespecial",
+        "invokestatic",
+        "invokeinterface",
+        "unused",
+        "new",
+        "newarray",
+        "anewarray",
+        "arraylength",
+        "athrow",
+        "checkcast",
+        "instanceof",
+        "monitorenter",
+        "monitorexit",
+        "wide",
+        "multianewarray",
+        "ifnull",
+        "ifnonnull",
+        "goto_w",
+        "jsr_w",
+    };
+
+
+    public static final byte ARRAY_T_BOOLEAN     = 4;
+    public static final byte ARRAY_T_CHAR        = 5;
+    public static final byte ARRAY_T_FLOAT       = 6;
+    public static final byte ARRAY_T_DOUBLE      = 7;
+    public static final byte ARRAY_T_BYTE        = 8;
+    public static final byte ARRAY_T_SHORT       = 9;
+    public static final byte ARRAY_T_INT         = 10;
+    public static final byte ARRAY_T_LONG        = 11;
+}
diff --git a/src/proguard/classfile/instruction/InstructionFactory.java b/src/proguard/classfile/instruction/InstructionFactory.java
new file mode 100644
index 0000000..b381db2
--- /dev/null
+++ b/src/proguard/classfile/instruction/InstructionFactory.java
@@ -0,0 +1,310 @@
+/* $Id: InstructionFactory.java,v 1.5 2004/08/21 21:37:28 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.instruction;
+
+/**
+ * This class provides methods to create and reuse Instruction objects.
+ *
+ * @author Eric Lafortune
+ */
+public class InstructionFactory
+{
+    // Shared copies of Instruction objects, to avoid creating a lot of objects.
+    private static final SimpleInstruction       simpleInstruction       = new SimpleInstruction();
+    private static final CpInstruction           cpInstruction           = new CpInstruction();
+    private static final VariableInstruction     variableInstruction     = new VariableInstruction();
+    private static final BranchInstruction       branchInstruction       = new BranchInstruction();
+    private static final TableSwitchInstruction  tableSwitchInstruction  = new TableSwitchInstruction();
+    private static final LookUpSwitchInstruction lookUpSwitchInstruction = new LookUpSwitchInstruction();
+
+
+    /**
+     * Creates a new Instruction from the data in the byte array, starting
+     * at the given index.
+     */
+    public static Instruction create(byte[] code, int offset)
+    {
+        // We'll re-use the static Instruction objects.
+        Instruction instruction;
+
+        int  index  = offset;
+        byte opcode = code[index++];
+
+        boolean wide = false;
+        if (opcode == InstructionConstants.OP_WIDE)
+        {
+            opcode = code[index++];
+            wide   = true;
+        }
+
+        switch (opcode)
+        {
+            // Simple instructions.
+            case InstructionConstants.OP_NOP:
+            case InstructionConstants.OP_ACONST_NULL:
+            case InstructionConstants.OP_ICONST_M1:
+            case InstructionConstants.OP_ICONST_0:
+            case InstructionConstants.OP_ICONST_1:
+            case InstructionConstants.OP_ICONST_2:
+            case InstructionConstants.OP_ICONST_3:
+            case InstructionConstants.OP_ICONST_4:
+            case InstructionConstants.OP_ICONST_5:
+            case InstructionConstants.OP_LCONST_0:
+            case InstructionConstants.OP_LCONST_1:
+            case InstructionConstants.OP_FCONST_0:
+            case InstructionConstants.OP_FCONST_1:
+            case InstructionConstants.OP_FCONST_2:
+            case InstructionConstants.OP_DCONST_0:
+            case InstructionConstants.OP_DCONST_1:
+
+            case InstructionConstants.OP_BIPUSH:
+            case InstructionConstants.OP_SIPUSH:
+
+            case InstructionConstants.OP_IALOAD:
+            case InstructionConstants.OP_LALOAD:
+            case InstructionConstants.OP_FALOAD:
+            case InstructionConstants.OP_DALOAD:
+            case InstructionConstants.OP_AALOAD:
+            case InstructionConstants.OP_BALOAD:
+            case InstructionConstants.OP_CALOAD:
+            case InstructionConstants.OP_SALOAD:
+
+            case InstructionConstants.OP_IASTORE:
+            case InstructionConstants.OP_LASTORE:
+            case InstructionConstants.OP_FASTORE:
+            case InstructionConstants.OP_DASTORE:
+            case InstructionConstants.OP_AASTORE:
+            case InstructionConstants.OP_BASTORE:
+            case InstructionConstants.OP_CASTORE:
+            case InstructionConstants.OP_SASTORE:
+            case InstructionConstants.OP_POP:
+            case InstructionConstants.OP_POP2:
+            case InstructionConstants.OP_DUP:
+            case InstructionConstants.OP_DUP_X1:
+            case InstructionConstants.OP_DUP_X2:
+            case InstructionConstants.OP_DUP2:
+            case InstructionConstants.OP_DUP2_X1:
+            case InstructionConstants.OP_DUP2_X2:
+            case InstructionConstants.OP_SWAP:
+            case InstructionConstants.OP_IADD:
+            case InstructionConstants.OP_LADD:
+            case InstructionConstants.OP_FADD:
+            case InstructionConstants.OP_DADD:
+            case InstructionConstants.OP_ISUB:
+            case InstructionConstants.OP_LSUB:
+            case InstructionConstants.OP_FSUB:
+            case InstructionConstants.OP_DSUB:
+            case InstructionConstants.OP_IMUL:
+            case InstructionConstants.OP_LMUL:
+            case InstructionConstants.OP_FMUL:
+            case InstructionConstants.OP_DMUL:
+            case InstructionConstants.OP_IDIV:
+            case InstructionConstants.OP_LDIV:
+            case InstructionConstants.OP_FDIV:
+            case InstructionConstants.OP_DDIV:
+            case InstructionConstants.OP_IREM:
+            case InstructionConstants.OP_LREM:
+            case InstructionConstants.OP_FREM:
+            case InstructionConstants.OP_DREM:
+            case InstructionConstants.OP_INEG:
+            case InstructionConstants.OP_LNEG:
+            case InstructionConstants.OP_FNEG:
+            case InstructionConstants.OP_DNEG:
+            case InstructionConstants.OP_ISHL:
+            case InstructionConstants.OP_LSHL:
+            case InstructionConstants.OP_ISHR:
+            case InstructionConstants.OP_LSHR:
+            case InstructionConstants.OP_IUSHR:
+            case InstructionConstants.OP_LUSHR:
+            case InstructionConstants.OP_IAND:
+            case InstructionConstants.OP_LAND:
+            case InstructionConstants.OP_IOR:
+            case InstructionConstants.OP_LOR:
+            case InstructionConstants.OP_IXOR:
+            case InstructionConstants.OP_LXOR:
+
+            case InstructionConstants.OP_I2L:
+            case InstructionConstants.OP_I2F:
+            case InstructionConstants.OP_I2D:
+            case InstructionConstants.OP_L2I:
+            case InstructionConstants.OP_L2F:
+            case InstructionConstants.OP_L2D:
+            case InstructionConstants.OP_F2I:
+            case InstructionConstants.OP_F2L:
+            case InstructionConstants.OP_F2D:
+            case InstructionConstants.OP_D2I:
+            case InstructionConstants.OP_D2L:
+            case InstructionConstants.OP_D2F:
+            case InstructionConstants.OP_I2B:
+            case InstructionConstants.OP_I2C:
+            case InstructionConstants.OP_I2S:
+            case InstructionConstants.OP_LCMP:
+            case InstructionConstants.OP_FCMPL:
+            case InstructionConstants.OP_FCMPG:
+            case InstructionConstants.OP_DCMPL:
+            case InstructionConstants.OP_DCMPG:
+
+            case InstructionConstants.OP_IRETURN:
+            case InstructionConstants.OP_LRETURN:
+            case InstructionConstants.OP_FRETURN:
+            case InstructionConstants.OP_DRETURN:
+            case InstructionConstants.OP_ARETURN:
+            case InstructionConstants.OP_RETURN:
+
+            case InstructionConstants.OP_NEWARRAY:
+            case InstructionConstants.OP_ARRAYLENGTH:
+            case InstructionConstants.OP_ATHROW:
+
+            case InstructionConstants.OP_MONITORENTER:
+            case InstructionConstants.OP_MONITOREXIT:
+                instruction = simpleInstruction;
+                break;
+
+            // Instructions with a contant pool index.
+            case InstructionConstants.OP_LDC:
+            case InstructionConstants.OP_LDC_W:
+            case InstructionConstants.OP_LDC2_W:
+
+            case InstructionConstants.OP_GETSTATIC:
+            case InstructionConstants.OP_PUTSTATIC:
+            case InstructionConstants.OP_GETFIELD:
+            case InstructionConstants.OP_PUTFIELD:
+
+            case InstructionConstants.OP_INVOKEVIRTUAL:
+            case InstructionConstants.OP_INVOKESPECIAL:
+            case InstructionConstants.OP_INVOKESTATIC:
+            case InstructionConstants.OP_INVOKEINTERFACE:
+
+            case InstructionConstants.OP_NEW:
+            case InstructionConstants.OP_ANEWARRAY:
+            case InstructionConstants.OP_CHECKCAST:
+            case InstructionConstants.OP_INSTANCEOF:
+            case InstructionConstants.OP_MULTIANEWARRAY:
+                instruction = cpInstruction;
+                break;
+
+            // Instructions with a local variable index.
+            case InstructionConstants.OP_ILOAD:
+            case InstructionConstants.OP_LLOAD:
+            case InstructionConstants.OP_FLOAD:
+            case InstructionConstants.OP_DLOAD:
+            case InstructionConstants.OP_ALOAD:
+            case InstructionConstants.OP_ILOAD_0:
+            case InstructionConstants.OP_ILOAD_1:
+            case InstructionConstants.OP_ILOAD_2:
+            case InstructionConstants.OP_ILOAD_3:
+            case InstructionConstants.OP_LLOAD_0:
+            case InstructionConstants.OP_LLOAD_1:
+            case InstructionConstants.OP_LLOAD_2:
+            case InstructionConstants.OP_LLOAD_3:
+            case InstructionConstants.OP_FLOAD_0:
+            case InstructionConstants.OP_FLOAD_1:
+            case InstructionConstants.OP_FLOAD_2:
+            case InstructionConstants.OP_FLOAD_3:
+            case InstructionConstants.OP_DLOAD_0:
+            case InstructionConstants.OP_DLOAD_1:
+            case InstructionConstants.OP_DLOAD_2:
+            case InstructionConstants.OP_DLOAD_3:
+            case InstructionConstants.OP_ALOAD_0:
+            case InstructionConstants.OP_ALOAD_1:
+            case InstructionConstants.OP_ALOAD_2:
+            case InstructionConstants.OP_ALOAD_3:
+
+            case InstructionConstants.OP_ISTORE:
+            case InstructionConstants.OP_LSTORE:
+            case InstructionConstants.OP_FSTORE:
+            case InstructionConstants.OP_DSTORE:
+            case InstructionConstants.OP_ASTORE:
+            case InstructionConstants.OP_ISTORE_0:
+            case InstructionConstants.OP_ISTORE_1:
+            case InstructionConstants.OP_ISTORE_2:
+            case InstructionConstants.OP_ISTORE_3:
+            case InstructionConstants.OP_LSTORE_0:
+            case InstructionConstants.OP_LSTORE_1:
+            case InstructionConstants.OP_LSTORE_2:
+            case InstructionConstants.OP_LSTORE_3:
+            case InstructionConstants.OP_FSTORE_0:
+            case InstructionConstants.OP_FSTORE_1:
+            case InstructionConstants.OP_FSTORE_2:
+            case InstructionConstants.OP_FSTORE_3:
+            case InstructionConstants.OP_DSTORE_0:
+            case InstructionConstants.OP_DSTORE_1:
+            case InstructionConstants.OP_DSTORE_2:
+            case InstructionConstants.OP_DSTORE_3:
+            case InstructionConstants.OP_ASTORE_0:
+            case InstructionConstants.OP_ASTORE_1:
+            case InstructionConstants.OP_ASTORE_2:
+            case InstructionConstants.OP_ASTORE_3:
+
+            case InstructionConstants.OP_IINC:
+
+            case InstructionConstants.OP_RET:
+                variableInstruction.wide = wide;
+                instruction = variableInstruction;
+                break;
+
+            // Instructions with a branch offset operand.
+            case InstructionConstants.OP_IFEQ:
+            case InstructionConstants.OP_IFNE:
+            case InstructionConstants.OP_IFLT:
+            case InstructionConstants.OP_IFGE:
+            case InstructionConstants.OP_IFGT:
+            case InstructionConstants.OP_IFLE:
+            case InstructionConstants.OP_IFICMPEQ:
+            case InstructionConstants.OP_IFICMPNE:
+            case InstructionConstants.OP_IFICMPLT:
+            case InstructionConstants.OP_IFICMPGE:
+            case InstructionConstants.OP_IFICMPGT:
+            case InstructionConstants.OP_IFICMPLE:
+            case InstructionConstants.OP_IFACMPEQ:
+            case InstructionConstants.OP_IFACMPNE:
+            case InstructionConstants.OP_GOTO:
+            case InstructionConstants.OP_JSR:
+
+            case InstructionConstants.OP_IFNULL:
+            case InstructionConstants.OP_IFNONNULL:
+
+            case InstructionConstants.OP_GOTO_W:
+            case InstructionConstants.OP_JSR_W:
+                instruction = branchInstruction;
+                break;
+
+            //  The tableswitch instruction.
+            case InstructionConstants.OP_TABLESWITCH:
+                instruction = tableSwitchInstruction;
+                break;
+
+            //  The lookupswitch instruction.
+            case InstructionConstants.OP_LOOKUPSWITCH:
+                instruction = lookUpSwitchInstruction;
+                break;
+
+            default:
+                throw new IllegalArgumentException("Unknown instruction ["+opcode+"] at offset "+offset);
+        }
+
+        instruction.opcode = opcode;
+
+        instruction.readInfo(code, index);
+
+        return instruction;
+    }
+}
diff --git a/src/proguard/classfile/instruction/InstructionVisitor.java b/src/proguard/classfile/instruction/InstructionVisitor.java
new file mode 100644
index 0000000..3484c64
--- /dev/null
+++ b/src/proguard/classfile/instruction/InstructionVisitor.java
@@ -0,0 +1,41 @@
+/* $Id: InstructionVisitor.java,v 1.13 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.instruction;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+
+
+/**
+ * This interface specifies the methods for a visitor of
+ * <code>Instruction</code> objects.
+ *
+ * @author Eric Lafortune
+ */
+public interface InstructionVisitor
+{
+    public void visitSimpleInstruction(      ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction       simpleInstruction);
+    public void visitVariableInstruction(    ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction     variableInstruction);
+    public void visitCpInstruction(          ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction           cpInstruction);
+    public void visitBranchInstruction(      ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction       branchInstruction);
+    public void visitTableSwitchInstruction( ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction  tableSwitchInstruction);
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction);
+}
diff --git a/src/proguard/classfile/instruction/LookUpSwitchInstruction.java b/src/proguard/classfile/instruction/LookUpSwitchInstruction.java
new file mode 100644
index 0000000..bb371f9
--- /dev/null
+++ b/src/proguard/classfile/instruction/LookUpSwitchInstruction.java
@@ -0,0 +1,142 @@
+/* $Id: LookUpSwitchInstruction.java,v 1.6 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.instruction;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+
+/**
+ * This Instruction represents a simple instruction without variable arguments
+ * or constant pool references.
+ *
+ * @author Eric Lafortune
+ */
+public class LookUpSwitchInstruction extends Instruction
+{
+    public int   defaultOffset;
+    public int   jumpOffsetCount;
+    public int[] cases;
+    public int[] jumpOffsets;
+
+
+    /**
+     * Creates an uninitialized LookUpSwitchInstruction.
+     */
+    public LookUpSwitchInstruction() {}
+
+
+    /**
+     * Copies the given instruction into this instruction.
+     * @param lookUpSwitchInstruction the instruction to be copied.
+     * @return this instruction.
+     */
+    public LookUpSwitchInstruction copy(LookUpSwitchInstruction lookUpSwitchInstruction)
+    {
+        this.opcode          = lookUpSwitchInstruction.opcode;
+        this.defaultOffset   = lookUpSwitchInstruction.defaultOffset;
+        this.jumpOffsetCount = lookUpSwitchInstruction.jumpOffsetCount;
+        this.cases           = lookUpSwitchInstruction.cases;
+        this.jumpOffsets     = lookUpSwitchInstruction.jumpOffsets;
+
+        return this;
+    }
+
+
+    // Implementations for Instruction.
+
+    public Instruction shrink()
+    {
+        // There aren't any ways to shrink this instruction.
+        return this;
+    }
+
+    protected void readInfo(byte[] code, int offset)
+    {
+        // Skip up to three padding bytes.
+        offset += -offset & 3;
+
+        // Read the two 32-bit arguments.
+        defaultOffset   = readInt(code, offset); offset += 4;
+        jumpOffsetCount = readInt(code, offset); offset += 4;
+
+        // Make sure there are sufficiently large match-offset tables.
+        if (jumpOffsets == null ||
+            jumpOffsets.length < jumpOffsetCount)
+        {
+            cases       = new int[jumpOffsetCount];
+            jumpOffsets = new int[jumpOffsetCount];
+        }
+
+        // Read the matches-offset pairs.
+        for (int index = 0; index < jumpOffsetCount; index++)
+        {
+            cases[index]       = readInt(code, offset); offset += 4;
+            jumpOffsets[index] = readInt(code, offset); offset += 4;
+        }
+    }
+
+
+    protected void writeInfo(byte[] code, int offset)
+    {
+        // Write up to three padding bytes.
+        while ((offset & 3) != 0)
+        {
+            writeByte(code, offset++, 0);
+        }
+
+        // Write the two 32-bit arguments.
+        writeInt(code, offset, defaultOffset);   offset += 4;
+        writeInt(code, offset, jumpOffsetCount); offset += 4;
+
+        // Write the matches-offset pairs.
+        for (int index = 0; index < jumpOffsetCount; index++)
+        {
+            writeInt(code, offset, cases[index]);       offset += 4;
+            writeInt(code, offset, jumpOffsets[index]); offset += 4;
+        }
+    }
+
+
+    public int length(int offset)
+    {
+        return 1 + (-(offset+1) & 3) + 8 + jumpOffsetCount * 8;
+    }
+
+
+    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, InstructionVisitor instructionVisitor)
+    {
+        instructionVisitor.visitLookUpSwitchInstruction(classFile, methodInfo, codeAttrInfo, offset, this);
+    }
+
+
+    public String toString(int offset)
+    {
+        return "["+offset+"] "+getName()+" (switchCaseCount="+jumpOffsetCount+")";
+    }
+
+
+    // Implementations for Object.
+
+    public String toString()
+    {
+        return getName()+" (switchCaseCount="+jumpOffsetCount+")";
+    }
+}
diff --git a/src/proguard/classfile/instruction/MultiInstructionVisitor.java b/src/proguard/classfile/instruction/MultiInstructionVisitor.java
new file mode 100644
index 0000000..5894285
--- /dev/null
+++ b/src/proguard/classfile/instruction/MultiInstructionVisitor.java
@@ -0,0 +1,138 @@
+/* $Id: MultiInstructionVisitor.java,v 1.3 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.instruction;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+
+
+/**
+ * This InstructionVisitor delegates all visits to each InstructionVisitor
+ * in a given list.
+ *
+ * @author Eric Lafortune
+ */
+public class MultiInstructionVisitor implements InstructionVisitor
+{
+    private static final int ARRAY_SIZE_INCREMENT = 5;
+
+    // Shared copies of Instruction objects, to avoid creating a lot of objects.
+    private final SimpleInstruction       simpleInstruction       = new SimpleInstruction();
+    private final CpInstruction           cpInstruction           = new CpInstruction();
+    private final VariableInstruction     variableInstruction     = new VariableInstruction();
+    private final BranchInstruction       branchInstruction       = new BranchInstruction();
+    private final TableSwitchInstruction  tableSwitchInstruction  = new TableSwitchInstruction();
+    private final LookUpSwitchInstruction lookUpSwitchInstruction = new LookUpSwitchInstruction();
+
+
+    private InstructionVisitor[] instructionVisitors;
+    private int                  instructionVisitorCount;
+
+
+    public MultiInstructionVisitor()
+    {
+    }
+
+
+    public MultiInstructionVisitor(InstructionVisitor[] instructionVisitors)
+    {
+        this.instructionVisitors     = instructionVisitors;
+        this.instructionVisitorCount = instructionVisitors.length;
+    }
+
+
+    public void addInstructionVisitor(InstructionVisitor instructionVisitor)
+    {
+        ensureArraySize();
+
+        instructionVisitors[instructionVisitorCount++] = instructionVisitor;
+    }
+
+
+    private void ensureArraySize()
+    {
+        if (instructionVisitors == null)
+        {
+            instructionVisitors = new InstructionVisitor[ARRAY_SIZE_INCREMENT];
+        }
+        else if (instructionVisitors.length == instructionVisitorCount)
+        {
+            InstructionVisitor[] newInstructionVisitors =
+                new InstructionVisitor[instructionVisitorCount +
+                                     ARRAY_SIZE_INCREMENT];
+            System.arraycopy(instructionVisitors, 0,
+                             newInstructionVisitors, 0,
+                             instructionVisitorCount);
+            instructionVisitors = newInstructionVisitors;
+        }
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
+    {
+        for (int index = 0; index < instructionVisitorCount; index++)
+        {
+            instructionVisitors[index].visitSimpleInstruction(classFile, methodInfo, codeAttrInfo, offset, this.simpleInstruction.copy(simpleInstruction));
+        }
+    }
+
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    {
+        for (int index = 0; index < instructionVisitorCount; index++)
+        {
+            instructionVisitors[index].visitVariableInstruction(classFile, methodInfo, codeAttrInfo, offset, this.variableInstruction.copy(variableInstruction));
+        }
+    }
+
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    {
+        for (int index = 0; index < instructionVisitorCount; index++)
+        {
+            instructionVisitors[index].visitCpInstruction(classFile, methodInfo, codeAttrInfo, offset, this.cpInstruction.copy(cpInstruction));
+        }
+    }
+
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+    {
+        for (int index = 0; index < instructionVisitorCount; index++)
+        {
+            instructionVisitors[index].visitBranchInstruction(classFile, methodInfo, codeAttrInfo, offset, this.branchInstruction.copy(branchInstruction));
+        }
+    }
+
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
+    {
+        for (int index = 0; index < instructionVisitorCount; index++)
+        {
+            instructionVisitors[index].visitTableSwitchInstruction(classFile, methodInfo, codeAttrInfo, offset, this.tableSwitchInstruction.copy(tableSwitchInstruction));
+        }
+    }
+
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
+    {
+        for (int index = 0; index < instructionVisitorCount; index++)
+        {
+            instructionVisitors[index].visitLookUpSwitchInstruction(classFile, methodInfo, codeAttrInfo, offset, this.lookUpSwitchInstruction.copy(lookUpSwitchInstruction));
+        }
+    }
+}
diff --git a/src/proguard/classfile/instruction/SimpleInstruction.java b/src/proguard/classfile/instruction/SimpleInstruction.java
new file mode 100644
index 0000000..90942a0
--- /dev/null
+++ b/src/proguard/classfile/instruction/SimpleInstruction.java
@@ -0,0 +1,182 @@
+/* $Id: SimpleInstruction.java,v 1.8 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.instruction;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+
+/**
+ * This Instruction represents a simple instruction without variable arguments
+ * or constant pool references.
+ *
+ * @author Eric Lafortune
+ */
+public class SimpleInstruction extends Instruction
+{
+    public int constant;
+
+
+    /**
+     * Creates an uninitialized SimpleInstruction.
+     */
+    public SimpleInstruction() {}
+
+
+    /**
+     * Creates a new SimpleInstruction with the given opcode.
+     */
+    public SimpleInstruction(byte opcode)
+    {
+        this(opcode, 0);
+    }
+
+
+    /**
+     * Creates a new SimpleInstruction with the given opcode and constant.
+     */
+    public SimpleInstruction(byte opcode, int constant)
+    {
+        this.opcode   = opcode;
+        this.constant = constant;
+    }
+
+
+    /**
+     * Copies the given instruction into this instruction.
+     * @param simpleInstruction the instruction to be copied.
+     * @return this instruction.
+     */
+    public SimpleInstruction copy(SimpleInstruction simpleInstruction)
+    {
+        this.opcode   = simpleInstruction.opcode;
+        this.constant = simpleInstruction.constant;
+
+        return this;
+    }
+
+
+    // Implementations for Instruction.
+
+    public Instruction shrink()
+    {
+        // Is this a sipush instruction that can be a bipush instruction?
+        if (opcode == InstructionConstants.OP_SIPUSH &&
+            constant << 24 >> 24 == constant)
+        {
+            opcode = InstructionConstants.OP_BIPUSH;
+        }
+
+        // Is this a bipush instruction that can be an iconst instruction?
+        if (opcode == InstructionConstants.OP_BIPUSH &&
+            constant >= -1 &&
+            constant <= 5)
+        {
+            opcode = (byte)(InstructionConstants.OP_ICONST_0 + constant);
+        }
+
+        return this;
+    }
+
+    protected void readInfo(byte[] code, int offset)
+    {
+        // Also initialize embedded constants that are different from 0.
+        switch (opcode)
+        {
+            case InstructionConstants.OP_ICONST_M1:
+                constant = -1;
+                break;
+
+            case InstructionConstants.OP_ICONST_1:
+            case InstructionConstants.OP_LCONST_1:
+            case InstructionConstants.OP_FCONST_1:
+            case InstructionConstants.OP_DCONST_1:
+                constant = 1;
+                break;
+
+            case InstructionConstants.OP_ICONST_2:
+            case InstructionConstants.OP_FCONST_2:
+                constant = 2;
+                break;
+
+            case InstructionConstants.OP_ICONST_3:
+                constant = 3;
+                break;
+
+            case InstructionConstants.OP_ICONST_4:
+                constant = 4;
+                break;
+
+            case InstructionConstants.OP_ICONST_5:
+                constant = 5;
+                break;
+
+            default:
+                constant = readSignedValue(code, offset, constantSize());
+                break;
+        }
+    }
+
+
+    protected void writeInfo(byte[] code, int offset)
+    {
+        writeValue(code, offset, constant, constantSize());
+    }
+
+
+    public int length(int offset)
+    {
+        return 1 + constantSize();
+    }
+
+
+    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, InstructionVisitor instructionVisitor)
+    {
+        instructionVisitor.visitSimpleInstruction(classFile, methodInfo, codeAttrInfo, offset, this);
+    }
+
+
+    public String toString(int offset)
+    {
+        return "["+offset+"] "+getName()+" (constant="+constant+")";
+    }
+
+
+    // Implementations for Object.
+
+    public String toString()
+    {
+        return getName()+" (constant="+constant+")";
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Computes the appropriate constant size for this instruction.
+     */
+    private int constantSize()
+    {
+        return opcode == InstructionConstants.OP_BIPUSH ||
+               opcode == InstructionConstants.OP_NEWARRAY ? 1 :
+               opcode == InstructionConstants.OP_SIPUSH   ? 2 :
+                                                            0;
+    }
+}
diff --git a/src/proguard/classfile/instruction/TableSwitchInstruction.java b/src/proguard/classfile/instruction/TableSwitchInstruction.java
new file mode 100644
index 0000000..a2b269f
--- /dev/null
+++ b/src/proguard/classfile/instruction/TableSwitchInstruction.java
@@ -0,0 +1,145 @@
+/* $Id: TableSwitchInstruction.java,v 1.6 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.instruction;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+
+/**
+ * This Instruction represents a simple instruction without variable arguments
+ * or constant pool references.
+ *
+ * @author Eric Lafortune
+ */
+public class TableSwitchInstruction extends Instruction
+{
+    public int   defaultOffset;
+    public int   lowCase;
+    public int   highCase;
+    public int   jumpOffsetCount;
+    public int[] jumpOffsets;
+
+
+    /**
+     * Creates an uninitialized TableSwitchInstruction.
+     */
+    public TableSwitchInstruction() {}
+
+
+    /**
+     * Copies the given instruction into this instruction.
+     * @param tableSwitchInstruction the instruction to be copied.
+     * @return this instruction.
+     */
+    public TableSwitchInstruction copy(TableSwitchInstruction tableSwitchInstruction)
+    {
+        this.opcode          = tableSwitchInstruction.opcode;
+        this.defaultOffset   = tableSwitchInstruction.defaultOffset;
+        this.lowCase         = tableSwitchInstruction.lowCase;
+        this.highCase        = tableSwitchInstruction.highCase;
+        this.jumpOffsetCount = tableSwitchInstruction.jumpOffsetCount;
+        this.jumpOffsets     = tableSwitchInstruction.jumpOffsets;
+
+        return this;
+    }
+
+
+    // Implementations for Instruction.
+
+    public Instruction shrink()
+    {
+        // There aren't any ways to shrink this instruction.
+        return this;
+    }
+
+    protected void readInfo(byte[] code, int offset)
+    {
+        // Skip up to three padding bytes.
+        offset += -offset & 3;
+
+        // Read the three 32-bit arguments.
+        defaultOffset = readInt(code, offset); offset += 4;
+        lowCase       = readInt(code, offset); offset += 4;
+        highCase      = readInt(code, offset); offset += 4;
+
+        // Make sure there is a sufficiently large jump offset table.
+        jumpOffsetCount = highCase - lowCase + 1;
+        if (jumpOffsets == null ||
+            jumpOffsets.length < jumpOffsetCount)
+        {
+            jumpOffsets = new int[jumpOffsetCount];
+        }
+
+        // Read the jump offsets.
+        for (int index = 0; index < jumpOffsetCount; index++)
+        {
+            jumpOffsets[index] = readInt(code, offset); offset += 4;
+        }
+    }
+
+
+    protected void writeInfo(byte[] code, int offset)
+    {
+        // Write up to three padding bytes.
+        while ((offset & 3) != 0)
+        {
+            writeByte(code, offset++, 0);
+        }
+
+        // Write the three 32-bit arguments.
+        writeInt(code, offset, defaultOffset); offset += 4;
+        writeInt(code, offset, lowCase);       offset += 4;
+        writeInt(code, offset, highCase);      offset += 4;
+
+        // Write the jump offsets.
+        int length = highCase - lowCase + 1;
+        for (int index = 0; index < length; index++)
+        {
+            writeInt(code, offset, jumpOffsets[index]); offset += 4;
+        }
+    }
+
+
+    public int length(int offset)
+    {
+        return 1 + (-(offset+1) & 3) + 12 + (highCase - lowCase + 1) * 4;
+    }
+
+
+    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, InstructionVisitor instructionVisitor)
+    {
+        instructionVisitor.visitTableSwitchInstruction(classFile, methodInfo, codeAttrInfo, offset, this);
+    }
+
+
+    public String toString(int offset)
+    {
+        return "["+offset+"] "+getName()+" (low="+lowCase+", high="+highCase+")";
+    }
+
+
+    // Implementations for Object.
+
+    public String toString()
+    {
+        return getName()+" (low="+lowCase+", high="+highCase+")";
+    }
+}
diff --git a/src/proguard/classfile/instruction/VariableInstruction.java b/src/proguard/classfile/instruction/VariableInstruction.java
new file mode 100644
index 0000000..7b60f6b
--- /dev/null
+++ b/src/proguard/classfile/instruction/VariableInstruction.java
@@ -0,0 +1,224 @@
+/* $Id: VariableInstruction.java,v 1.17 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.instruction;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+
+/**
+ * This Instruction represents an instruction that refers to a variable on the
+ * local variable stack.
+ *
+ * @author Eric Lafortune
+ */
+public class VariableInstruction extends Instruction
+{
+    public boolean wide;
+    public int     variableIndex;
+    public int     constant;
+
+
+    /**
+     * Creates an uninitialized VariableInstruction.
+     */
+    public VariableInstruction() {}
+
+
+    public VariableInstruction(byte opcode,
+                               int  variableIndex)
+    {
+        this(opcode, variableIndex, 0);
+    }
+
+
+    public VariableInstruction(byte opcode,
+                               int  variableIndex,
+                               int  constant)
+    {
+        this.opcode        = opcode;
+        this.variableIndex = variableIndex;
+        this.constant      = constant;
+        this.wide          = mustBeWide();
+    }
+
+
+    /**
+     * Copies the given instruction into this instruction.
+     * @param variableInstruction the instruction to be copied.
+     * @return this instruction.
+     */
+    public VariableInstruction copy(VariableInstruction variableInstruction)
+    {
+        this.opcode        = variableInstruction.opcode;
+        this.variableIndex = variableInstruction.variableIndex;
+        this.constant      = variableInstruction.constant;
+        this.wide          = variableInstruction.wide;
+
+        return this;
+    }
+
+
+    /**
+     * Returns whether this instruction loads the value of a variable onto the
+     * stack, or if it stores one.
+     */
+    public boolean isLoad()
+    {
+        // A load instruction can be recognized as follows.
+        // Note that this includes the ret instruction, which has a negative opcode.
+        // The iinc instruction is considered a store instruction.
+        return opcode <  InstructionConstants.OP_ISTORE &&
+               opcode != InstructionConstants.OP_IINC;
+    }
+
+
+    // Implementations for Instruction.
+
+    public Instruction shrink()
+    {
+        // Is this instruction pointing to a variable with index from 0 to 3?
+        if (variableIndex <= 3)
+        {
+            switch (opcode)
+            {
+                case InstructionConstants.OP_ILOAD: opcode = (byte)(InstructionConstants.OP_ILOAD_0 + variableIndex); break;
+                case InstructionConstants.OP_LLOAD: opcode = (byte)(InstructionConstants.OP_LLOAD_0 + variableIndex); break;
+                case InstructionConstants.OP_FLOAD: opcode = (byte)(InstructionConstants.OP_FLOAD_0 + variableIndex); break;
+                case InstructionConstants.OP_DLOAD: opcode = (byte)(InstructionConstants.OP_DLOAD_0 + variableIndex); break;
+                case InstructionConstants.OP_ALOAD: opcode = (byte)(InstructionConstants.OP_ALOAD_0 + variableIndex); break;
+
+                case InstructionConstants.OP_ISTORE: opcode = (byte)(InstructionConstants.OP_ISTORE_0 + variableIndex); break;
+                case InstructionConstants.OP_LSTORE: opcode = (byte)(InstructionConstants.OP_LSTORE_0 + variableIndex); break;
+                case InstructionConstants.OP_FSTORE: opcode = (byte)(InstructionConstants.OP_FSTORE_0 + variableIndex); break;
+                case InstructionConstants.OP_DSTORE: opcode = (byte)(InstructionConstants.OP_DSTORE_0 + variableIndex); break;
+                case InstructionConstants.OP_ASTORE: opcode = (byte)(InstructionConstants.OP_ASTORE_0 + variableIndex); break;
+            }
+        }
+
+        // Only make the instruction wide if necessary.
+        wide = mustBeWide();
+
+        return this;
+    }
+
+
+    protected boolean isWide()
+    {
+        return wide;
+    }
+
+
+    protected void readInfo(byte[] code, int offset)
+    {
+        int variableIndexSize = variableIndexSize();
+        int constantSize      = constantSize();
+
+        // Also initialize embedded variable indexes.
+        if (variableIndexSize == 0)
+        {
+            // An embedded variable index can be decoded as follows.
+            variableIndex = opcode < InstructionConstants.OP_ISTORE_0 ?
+                (opcode - InstructionConstants.OP_ILOAD_0 ) & 3 :
+                (opcode - InstructionConstants.OP_ISTORE_0) & 3;
+        }
+        else
+        {
+            variableIndex = readValue(code, offset, variableIndexSize); offset += variableIndexSize;
+        }
+
+        constant = readSignedValue(code, offset, constantSize);
+    }
+
+
+    protected void writeInfo(byte[] code, int offset)
+    {
+        int variableIndexSize = variableIndexSize();
+        int constantSize      = constantSize();
+
+        writeValue(code, offset, variableIndex, variableIndexSize); offset += variableIndexSize;
+        writeValue(code, offset, constant,      constantSize);
+    }
+
+
+    public int length(int offset)
+    {
+        return (wide ? 2 : 1) + variableIndexSize() + constantSize();
+    }
+
+
+    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, InstructionVisitor instructionVisitor)
+    {
+        instructionVisitor.visitVariableInstruction(classFile, methodInfo, codeAttrInfo, offset, this);
+    }
+
+
+    public String toString(int offset)
+    {
+        return "["+offset+"] "+getName()+(wide?"_w":"")+" (variableIndex="+variableIndex+", constant="+constant+")";
+    }
+
+
+    // Implementations for Object.
+
+    public String toString()
+    {
+        return getName()+" (variableIndex="+variableIndex+", constant="+constant+")";
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns whether this instruction must be wide due to its variable index
+     * and constant.
+     */
+    private boolean mustBeWide()
+    {
+        return variableIndex > 0xff ||
+               (opcode == InstructionConstants.OP_IINC ? (constant < -128 ||
+                                                          constant > 127) :
+                                                         constant > 255);
+    }
+
+
+    /**
+     * Returns the appropriate variable index size for this instruction.
+     */
+    private int variableIndexSize()
+    {
+        return (opcode >= InstructionConstants.OP_ILOAD_0 &&
+                opcode <= InstructionConstants.OP_ALOAD_3) ||
+               (opcode >= InstructionConstants.OP_ISTORE_0 &&
+                opcode <= InstructionConstants.OP_ASTORE_3) ? 0 :
+               wide                                         ? 2 :
+                                                              1;
+    }
+
+
+    /**
+     * Returns the appropriate constant size for this instruction.
+     */
+    private int constantSize()
+    {
+        return opcode == InstructionConstants.OP_IINC ? (wide ? 2 : 1) :
+                                                        0;
+    }
+}
diff --git a/src/proguard/classfile/instruction/package.html b/src/proguard/classfile/instruction/package.html
new file mode 100644
index 0000000..48c234e
--- /dev/null
+++ b/src/proguard/classfile/instruction/package.html
@@ -0,0 +1,9 @@
+<body>
+This package contains classes to represent Java bytecode instructions.
+<p>
+Not every instruction currently has its own class. Only groups of instructions
+that refer to the constant pool get their own representations.
+<p>
+While the package is sufficient for the current needs of the ProGuard
+application, it may very well be reorganised and extended in the future.
+</body>
diff --git a/src/proguard/classfile/package.html b/src/proguard/classfile/package.html
new file mode 100644
index 0000000..fad087c
--- /dev/null
+++ b/src/proguard/classfile/package.html
@@ -0,0 +1,15 @@
+<body>
+This package contains classes to represent the various elements of class files.
+<p>
+A class file is represented by the <code>{@link proguard.classfile.ClassFile
+ClassFile}</code> interface. This interface currently has two alternative
+representations:
+<ul>
+<li><code>{@link ProgramClassFile ProgramClassFile}</code>:
+    a complete representation that can be read, modified, and written back.
+<li><code>{@link LibraryClassFile LibraryClassFile}</code>:
+    an incomplete representation that can be only be read. It is however
+    more compact than <code>ProgramClassFile</code>, and sufficient for
+    analyzing class files from library jars.
+</ul>
+</body>
diff --git a/src/proguard/classfile/util/AccessUtil.java b/src/proguard/classfile/util/AccessUtil.java
new file mode 100644
index 0000000..af7c034
--- /dev/null
+++ b/src/proguard/classfile/util/AccessUtil.java
@@ -0,0 +1,99 @@
+/* $Id: AccessUtil.java,v 1.3 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+
+
+/**
+ * Utility methods for working with access flags. For convenience, this class
+ * defines access levels, in ascending order: <code>PRIVATE</code>,
+ * <code>PACKAGE_VISIBLE</code>, <code>PROTECTED</code>, and <code>PUBLIC</code>.
+ *
+ * @author Eric Lafortune
+ */
+public class AccessUtil
+{
+    public static final int PRIVATE         = 0;
+    public static final int PACKAGE_VISIBLE = 1;
+    public static final int PROTECTED       = 2;
+    public static final int PUBLIC          = 3;
+
+
+    // The mask of access flags.
+    private static final int ACCESS_MASK =
+        ClassConstants.INTERNAL_ACC_PUBLIC  |
+        ClassConstants.INTERNAL_ACC_PRIVATE |
+        ClassConstants.INTERNAL_ACC_PROTECTED;
+
+
+    /**
+     * Returns the corresponding access level of the given access flags.
+     * @param accessFlags the internal access flags.
+     * @return the corresponding access level: <code>PRIVATE</code>,
+     *         <code>PACKAGE_VISIBLE</code>, <code>PROTECTED</code>, or
+     *         <code>PUBLIC</code>.
+     */
+    public static int accessLevel(int accessFlags)
+    {
+        switch (accessFlags & ACCESS_MASK)
+        {
+            case ClassConstants.INTERNAL_ACC_PRIVATE:   return PRIVATE;
+            default:                                    return PACKAGE_VISIBLE;
+            case ClassConstants.INTERNAL_ACC_PROTECTED: return PROTECTED;
+            case ClassConstants.INTERNAL_ACC_PUBLIC:    return PUBLIC;
+        }
+    }
+
+
+    /**
+     * Returns the corresponding access flags of the given access level.
+     * @param accessLevel the access level: <code>PRIVATE</code>,
+     *                    <code>PACKAGE_VISIBLE</code>, <code>PROTECTED</code>,
+     *                    or <code>PUBLIC</code>.
+     * @return the corresponding internal access flags,  the internal access
+     *         flags as a logical bit mask of <code>INTERNAL_ACC_PRIVATE</code>,
+     *         <code>INTERNAL_ACC_PROTECTED</code>, and
+     *         <code>INTERNAL_ACC_PUBLIC</code>.
+     */
+    public static int accessFlags(int accessLevel)
+    {
+        switch (accessLevel)
+        {
+            case PRIVATE:         return ClassConstants.INTERNAL_ACC_PRIVATE;
+            default:              return 0;
+            case PROTECTED:       return ClassConstants.INTERNAL_ACC_PROTECTED;
+            case PUBLIC:          return ClassConstants.INTERNAL_ACC_PUBLIC;
+        }
+    }
+
+
+    /**
+     * Replaces the access part of the given access flags.
+     * @param accessFlags the internal access flags.
+     * @param accessFlags the new internal access flags.
+     */
+    public static int replaceAccessFlags(int accessFlags, int newAccessFlags)
+    {
+        return (accessFlags    & ~ACCESS_MASK) |
+               (newAccessFlags &  ACCESS_MASK);
+    }
+}
diff --git a/src/proguard/classfile/util/ClassFileClassForNameReferenceInitializer.java b/src/proguard/classfile/util/ClassFileClassForNameReferenceInitializer.java
new file mode 100644
index 0000000..caeac24
--- /dev/null
+++ b/src/proguard/classfile/util/ClassFileClassForNameReferenceInitializer.java
@@ -0,0 +1,260 @@
+/* $Id: ClassFileClassForNameReferenceInitializer.java,v 1.14 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This InstructionVisitor initializes any special <code>Class.forName</code> or
+ * <code>.class</code> references of all class files it visits. More specifically,
+ * it fills out the references of String constant pool entries that refer to a
+ * class file in the program class pool.
+ * <p>
+ * It optionally prints notes if on usage of
+ * <code>(SomeClass)Class.forName(variable).newInstance()</code>.
+ * <p>
+ * The class file hierarchy must be initialized before using this visitor.
+ *
+ * @see ClassFileReferenceInitializer
+ *
+ * @author Eric Lafortune
+ */
+class      ClassFileClassForNameReferenceInitializer
+implements InstructionVisitor,
+           CpInfoVisitor
+{
+    private ClassPool programClassPool;
+    private boolean   note;
+
+    // Counter for notes.
+    private int       noteCount;
+
+    // Fields to remember the previous StringCpInfo and MethodRefCpInfo objects
+    // while visiting all instructions (to find Class.forName, class$, and
+    // Class.newInstance invocations, and possible class casts afterwards).
+    private int ldcStringCpIndex              = -1;
+    private int invokestaticMethodRefCpIndex  = -1;
+    private int invokevirtualMethodRefCpIndex = -1;
+
+    private ClassForNameChecker     classForNameChecker     = new ClassForNameChecker();
+    private ClassNewInstanceChecker classNewInstanceChecker = new ClassNewInstanceChecker();
+
+
+    /**
+     * Creates a new ClassFileClassForNameReferenceInitializer that prints notes.
+     */
+    public ClassFileClassForNameReferenceInitializer(ClassPool programClassPool)
+    {
+        this(programClassPool, true);
+    }
+
+
+    /**
+     * Creates a new ClassFileClassForNameReferenceInitializer that optionally
+     * prints notes.
+     */
+    public ClassFileClassForNameReferenceInitializer(ClassPool programClassPool,
+                                                     boolean   note)
+    {
+        this.programClassPool = programClassPool;
+        this.note             = note;
+    }
+
+
+    /**
+     * Returns the number of notes printed about occurrences of
+     * '<code>(SomeClass)Class.forName(variable).newInstance()</code>'.
+     */
+    public int getNoteCount()
+    {
+        return noteCount;
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+
+
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    {
+        // Just ignore generic instructions and reset the constant pool indices.
+        switch (variableInstruction.opcode)
+        {
+            case InstructionConstants.OP_ICONST_0:
+            case InstructionConstants.OP_ICONST_1:
+                // Still remember any loaded string; this instruction may be
+                // setting up the second argument for class$(String, boolean).
+                break;
+
+            default:
+                ldcStringCpIndex = -1;
+                break;
+        }
+
+        invokestaticMethodRefCpIndex  = -1;
+        invokevirtualMethodRefCpIndex = -1;
+    }
+
+
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    {
+        int currentCpIndex = cpInstruction.cpIndex;
+
+        switch (cpInstruction.opcode)
+        {
+            case InstructionConstants.OP_LDC:
+            case InstructionConstants.OP_LDC_W:
+                // Are we loading a constant String?
+                int currentCpTag = classFile.getCpTag(currentCpIndex);
+                if (currentCpTag == ClassConstants.CONSTANT_String)
+                {
+                    // Remember it; it might be the argument of
+                    // Class.forName(String), class$(String), or
+                    // class$(String, boolean).
+                    ldcStringCpIndex = currentCpIndex;
+                }
+                invokestaticMethodRefCpIndex  = -1;
+                invokevirtualMethodRefCpIndex = -1;
+                break;
+
+            case InstructionConstants.OP_INVOKESTATIC:
+                // Are we invoking a static method that might have a constant
+                // String argument?
+                if (ldcStringCpIndex > 0)
+                {
+                    // Check whether the method reference points to Class.forName.
+                    if (classForNameChecker.check(classFile, currentCpIndex))
+                    {
+                        // Fill out the class file reference in the String.
+                        classFile.constantPoolEntryAccept(ldcStringCpIndex, this);
+                    }
+
+                    // We've dealt with this invocation, so we can forget about it.
+                    invokestaticMethodRefCpIndex = -1;
+                }
+                else
+                {
+                    // Remember it; it might still be a Class.forName with a
+                    // variable String argument.
+                    invokestaticMethodRefCpIndex = currentCpIndex;
+                }
+
+                ldcStringCpIndex              = -1;
+                invokevirtualMethodRefCpIndex = -1;
+                break;
+
+            case InstructionConstants.OP_INVOKEVIRTUAL:
+                // Are we invoking a virtual method right after a static method?
+                if (invokestaticMethodRefCpIndex > 0)
+                {
+                    // Remember it; it might be Class.newInstance after a Class.forName.
+                    invokevirtualMethodRefCpIndex = currentCpIndex;
+                }
+                else
+                {
+                    invokestaticMethodRefCpIndex  = -1;
+                    invokevirtualMethodRefCpIndex = -1;
+                }
+
+                ldcStringCpIndex = -1;
+                break;
+
+            case InstructionConstants.OP_CHECKCAST:
+                // Are we checking a cast right after a static method and a
+                // virtual method?
+                if (invokestaticMethodRefCpIndex  > 0 &&
+                    invokevirtualMethodRefCpIndex > 0)
+                {
+                    // Check whether the first method reference points to Class.forName,
+                    // and the second method reference to Class.newInstance.
+                    if (classForNameChecker.check(classFile, invokestaticMethodRefCpIndex) &&
+                        classNewInstanceChecker.check(classFile, invokevirtualMethodRefCpIndex))
+                    {
+                        // Note which class is being cast to.
+                        classFile.constantPoolEntryAccept(currentCpIndex, this);
+                    }
+                }
+
+                ldcStringCpIndex              = -1;
+                invokestaticMethodRefCpIndex  = -1;
+                invokevirtualMethodRefCpIndex = -1;
+                break;
+
+            default:
+                // Nothing interesting; just forget about previous indices.
+                ldcStringCpIndex              = -1;
+                invokestaticMethodRefCpIndex  = -1;
+                invokevirtualMethodRefCpIndex = -1;
+                break;
+        }
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+
+
+    /**
+     * Fills out the link to the referenced ClassFile.
+     */
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
+    {
+        // Save a reference to the corresponding class file.
+        String externalClassName = stringCpInfo.getString(classFile);
+        String internalClassName = ClassUtil.internalClassName(externalClassName);
+
+        stringCpInfo.referencedClassFile = programClassPool.getClass(internalClassName);
+    }
+
+
+    /**
+     * Prints out a note about the class cast to this class, if applicable.
+     */
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    {
+        if (note)
+        {
+            noteCount++;
+            System.err.println("Note: " +
+                               ClassUtil.externalClassName(classFile.getName()) +
+                               " calls '(" +
+                               ClassUtil.externalClassName(classCpInfo.getName(classFile)) +
+                               ")Class.forName(variable).newInstance()'");
+        }
+    }
+}
diff --git a/src/proguard/classfile/util/ClassFileHierarchyInitializer.java b/src/proguard/classfile/util/ClassFileHierarchyInitializer.java
new file mode 100644
index 0000000..04941ec
--- /dev/null
+++ b/src/proguard/classfile/util/ClassFileHierarchyInitializer.java
@@ -0,0 +1,282 @@
+/* $Id: ClassFileHierarchyInitializer.java,v 1.10 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This ClassFileVisitor initializes the class hierarchy of all class files that
+ * it visits.
+ * <p>
+ * Visited class files are added to the subclass list of their superclasses and
+ * interfaces. These subclass lists make it more convenient to travel down the
+ * class hierarchy.
+ * <p>
+ * Visited library class files get direct references to their superclasses and
+ * interfaces, replacing the superclass names and interface names. The direct
+ * references are equivalent to the names, but they are more efficient to work
+ * with.
+ * <p>
+ * This visitor optionally prints warnings if some items can't be found.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassFileHierarchyInitializer
+  implements ClassFileVisitor,
+             CpInfoVisitor
+{
+    // A visitor info flag to indicate the class file has been initialized.
+    private static final Object INITIALIZED = new Object();
+
+    private ClassPool programClassPool;
+    private ClassPool libraryClassPool;
+    private boolean   warn;
+
+    // Counter for warnings.
+    private int warningCount;
+
+
+    /**
+     * Creates a new ClassFileReferenceInitializer that initializes the hierarchy
+     * of all visited class files, printing warnings if some classes can't be found.
+     */
+    public ClassFileHierarchyInitializer(ClassPool programClassPool,
+                                         ClassPool libraryClassPool)
+    {
+        this(programClassPool, libraryClassPool, true);
+    }
+
+
+    /**
+     * Creates a new ClassFileReferenceInitializer that initializes the hierarchy
+     * of all visited class files, optionally printing warnings if some classes
+     * can't be found.
+     */
+    public ClassFileHierarchyInitializer(ClassPool programClassPool,
+                                ClassPool libraryClassPool,
+                                boolean   warn)
+    {
+        this.programClassPool = programClassPool;
+        this.libraryClassPool = libraryClassPool;
+        this.warn             = warn;
+    }
+
+
+    /**
+     * Returns the number of warnings printed about unresolved references to
+     * superclasses or interfaces.
+     */
+    public int getWarningCount()
+    {
+        return warningCount;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Haven't we initialized this class before?
+        if (!isInitialized(programClassFile))
+        {
+            // Mark this class.
+            markAsInitialized(programClassFile);
+
+            if (programClassFile.u2superClass != 0)
+            {
+                programClassFile.constantPoolEntryAccept(programClassFile.u2superClass,
+                                                         this);
+            }
+
+            // Add this class to the subclasses of its superclass.
+            if (programClassFile.u2superClass != 0)
+            {
+                addSubclass(programClassFile,
+                            programClassFile.getSuperClass(),
+                            programClassFile.getSuperName());
+            }
+
+            // Add this class to the subclasses of its interfaces.
+            for (int index = 0; index < programClassFile.u2interfacesCount; index++)
+            {
+                programClassFile.constantPoolEntryAccept(programClassFile.u2interfaces[index],
+                                                         this);
+
+                addSubclass(programClassFile,
+                            programClassFile.getInterface(index),
+                            programClassFile.getInterfaceName(index));
+            }
+        }
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        // Haven't we initialized this class before?
+        if (!isInitialized(libraryClassFile))
+        {
+            // Mark this class.
+            markAsInitialized(libraryClassFile);
+
+            // Have a closer look at the superclass.
+            String superClassName = libraryClassFile.superClassName;
+            if (superClassName != null)
+            {
+                // Find and initialize the super class.
+                ClassFile superClass = findAndInitializeClass(superClassName);
+
+                // Add this class to the subclasses of its superclass,
+                addSubclass(libraryClassFile,
+                            superClass,
+                            superClassName);
+
+                // Keep a reference to the superclass.
+                libraryClassFile.superClass = superClass;
+            }
+
+            // Have a closer look at the interface classes.
+            if (libraryClassFile.interfaceNames != null)
+            {
+                String[]    interfaceNames   = libraryClassFile.interfaceNames;
+                ClassFile[] interfaceClasses = new ClassFile[interfaceNames.length];
+
+                for (int index = 0; index < interfaceNames.length; index++)
+                {
+                    // Find and initialize the interface class.
+                    String    interfaceName  = interfaceNames[index];
+                    ClassFile interfaceClass = findAndInitializeClass(interfaceName);
+
+                    // Add this class to the subclasses of the interface class.
+                    addSubclass(libraryClassFile,
+                                interfaceClass,
+                                interfaceName);
+
+                    // Keep a reference to the interface class.
+                    interfaceClasses[index] = interfaceClass;
+                }
+
+                libraryClassFile.interfaceClasses = interfaceClasses;
+            }
+
+            // Discard the name Strings. From now on, we'll use the object
+            // references.
+            libraryClassFile.superClassName = null;
+            libraryClassFile.interfaceNames = null;
+        }
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+
+
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    {
+        classCpInfo.referencedClassFile =
+            findAndInitializeClass(classCpInfo.getName(classFile));
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Finds and initializes a class with the given name.
+     *
+     * @see #findClass(String)
+     */
+    private ClassFile findAndInitializeClass(String name)
+    {
+        // Try to find the class file.
+        ClassFile referencedClassFile = findClass(name);
+
+        // Did we find the referenced class file in either class pool?
+        if (referencedClassFile != null)
+        {
+            // Initialize it.
+            referencedClassFile.accept(this);
+        }
+
+        return referencedClassFile;
+    }
+
+
+    /**
+     * Returns the class with the given name, either for the program class pool
+     * or from the library class pool, or <code>null</code> if it can't be found.
+     */
+    private ClassFile findClass(String name)
+    {
+        // First look for the class in the program class pool.
+        ClassFile classFile = programClassPool.getClass(name);
+
+        // Otherwise look for the class in the library class pool.
+        if (classFile == null)
+        {
+            classFile = libraryClassPool.getClass(name);
+        }
+
+        return classFile;
+    }
+
+
+    private static void markAsInitialized(VisitorAccepter visitorAccepter)
+    {
+        visitorAccepter.setVisitorInfo(INITIALIZED);
+    }
+
+
+    private static boolean isInitialized(VisitorAccepter visitorAccepter)
+    {
+        return visitorAccepter.getVisitorInfo() == INITIALIZED;
+    }
+
+
+    private void addSubclass(ClassFile subclass,
+                             ClassFile classFile,
+                             String    className)
+    {
+        if (classFile != null)
+        {
+            classFile.addSubClass(subclass);
+        }
+        else if (warn)
+        {
+            // We didn't find the superclass or interface. Print a warning.
+            warningCount++;
+            System.err.println("Warning: " +
+                               ClassUtil.externalClassName(subclass.getName()) +
+                               ": can't find superclass or interface " +
+                               ClassUtil.externalClassName(className));
+        }
+    }
+}
diff --git a/src/proguard/classfile/util/ClassFileReferenceInitializer.java b/src/proguard/classfile/util/ClassFileReferenceInitializer.java
new file mode 100644
index 0000000..717fcd2
--- /dev/null
+++ b/src/proguard/classfile/util/ClassFileReferenceInitializer.java
@@ -0,0 +1,523 @@
+/* $Id: ClassFileReferenceInitializer.java,v 1.28 2004/11/27 10:09:26 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This ClassFileVisitor initializes the references of all class files that
+ * it visits.
+ * <p>
+ * All class constant pool entries get direct references to the corresponding
+ * classes. These references make it more convenient to travel up and across
+ * the class hierarchy.
+ * <p>
+ * All field and method reference constant pool entries get direct references
+ * to the corresponding classes, fields, and methods.
+ * <p>
+ * All name and type constant pool entries get a list of direct references to
+ * the classes listed in the type.
+ * <p>
+ * This visitor optionally prints warnings if some items can't be found, and
+ * notes on the usage of <code>(SomeClass)Class.forName(variable).newInstance()</code>.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassFileReferenceInitializer
+  implements ClassFileVisitor,
+             MemberInfoVisitor,
+             CpInfoVisitor,
+             AttrInfoVisitor,
+             //LocalVariableInfoVisitor,
+             //LocalVariableTypeInfoVisitor,
+             AnnotationVisitor,
+             ElementValueVisitor
+{
+    // A reusable object for checking whether referenced methods are Class.forName,...
+    private ClassFileClassForNameReferenceInitializer classFileClassForNameReferenceInitializer;
+
+    private MemberFinder memberFinder = new MemberFinder();
+
+    private ClassPool programClassPool;
+    private ClassPool libraryClassPool;
+    private boolean   warn;
+
+    // Counter for warnings.
+    private int warningCount;
+
+
+    /**
+     * Creates a new ClassFileReferenceInitializer that initializes the hierarchy
+     * of all visited class files, printing warnings if some classes can't be found.
+     */
+    public ClassFileReferenceInitializer(ClassPool programClassPool,
+                                         ClassPool libraryClassPool)
+    {
+        this(programClassPool, libraryClassPool, true, true);
+    }
+
+
+    /**
+     * Creates a new ClassFileReferenceInitializer that initializes the hierarchy
+     * of all visited class files, optionally printing warnings if some classes
+     * can't be found.
+     */
+    public ClassFileReferenceInitializer(ClassPool programClassPool,
+                                         ClassPool libraryClassPool,
+                                         boolean   warn,
+                                         boolean   note)
+    {
+        this.programClassPool = programClassPool;
+        this.libraryClassPool = libraryClassPool;
+        this.warn             = warn;
+
+        classFileClassForNameReferenceInitializer =
+            new ClassFileClassForNameReferenceInitializer(programClassPool, note);
+    }
+
+
+    /**
+     * Returns the number of warnings printed about unresolved references to
+     * class members in program class files.
+     */
+    public int getWarningCount()
+    {
+        return warningCount;
+    }
+
+
+    /**
+     * Returns the number of notes printed about occurrences of
+     * '<code>(SomeClass)Class.forName(variable).newInstance()</code>'.
+     */
+    public int getNoteCount()
+    {
+        return classFileClassForNameReferenceInitializer.getNoteCount();
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Initialize the constant pool entries.
+        programClassFile.constantPoolEntriesAccept(this);
+
+        // Initialize all fields and methods.
+        programClassFile.fieldsAccept(this);
+        programClassFile.methodsAccept(this);
+
+        // Initialize the attributes.
+        programClassFile.attributesAccept(this);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        visitMemberInfo(programClassFile, programFieldInfo);
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        visitMemberInfo(programClassFile, programMethodInfo);
+    }
+
+
+    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    {
+        programMemberInfo.referencedClassFiles =
+            findReferencedClasses(programMemberInfo.getDescriptor(programClassFile));
+
+        // Initialize the attributes.
+        programMemberInfo.attributesAccept(programClassFile, this);
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+
+
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    {
+        visitRefCpInfo(classFile, fieldrefCpInfo, true);
+    }
+
+
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    {
+        visitRefCpInfo(classFile, interfaceMethodrefCpInfo, false);
+    }
+
+
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    {
+        visitRefCpInfo(classFile, methodrefCpInfo, false);
+    }
+
+
+    private void visitRefCpInfo(ClassFile classFile, RefCpInfo refCpInfo, boolean isFieldRef)
+    {
+        String className = refCpInfo.getClassName(classFile);
+
+        // See if we can find the referenced class file.
+        // Unresolved references are assumed to refer to library class files
+        // that will not change anyway.
+        ClassFile referencedClassFile = findClass(className);
+
+        if (referencedClassFile != null)
+        {
+            String name = refCpInfo.getName(classFile);
+            String type = refCpInfo.getType(classFile);
+
+            // See if we can find the referenced class membver somewhere in the
+            // hierarchy.
+            refCpInfo.referencedMemberInfo = memberFinder.findMember(referencedClassFile,
+                                                                     name,
+                                                                     type,
+                                                                     isFieldRef);
+            refCpInfo.referencedClassFile  = memberFinder.correspondingClassFile();
+
+            if (warn && refCpInfo.referencedMemberInfo == null)
+            {
+                // We've haven't found the class member anywhere in the hierarchy.
+                warningCount++;
+                System.err.println("Warning: " +
+                                   ClassUtil.externalClassName(classFile.getName()) +
+                                   ": can't find referenced " +
+                                   (isFieldRef ?
+                                    "field '"  + ClassUtil.externalFullFieldDescription(0, name, type) :
+                                    "method '" + ClassUtil.externalFullMethodDescription(className, 0, name, type)) +
+                                   "' in class " +
+                                   ClassUtil.externalClassName(className));
+            }
+        }
+    }
+
+
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    {
+        classCpInfo.referencedClassFile =
+            findClass(classCpInfo.getName(classFile));
+    }
+
+
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo)
+    {
+        nameAndTypeCpInfo.referencedClassFiles =
+            findReferencedClasses(nameAndTypeCpInfo.getType(classFile));
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
+
+
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
+    {
+        String className = enclosingMethodAttrInfo.getClassName(classFile);
+
+        // See if we can find the referenced class file.
+        ClassFile referencedClassFile = findClass(className);
+
+        if (referencedClassFile == null)
+        {
+            // We couldn't find the enclosing class.
+            if (warn)
+            {
+                warningCount++;
+                System.err.println("Warning: " +
+                                   ClassUtil.externalClassName(classFile.getName()) +
+                                   ": can't find enclosing class " +
+                                   ClassUtil.externalClassName(className));
+            }
+
+            return;
+        }
+
+        // Make sure there is actually an enclosed method.
+        if (enclosingMethodAttrInfo.u2nameAndTypeIndex == 0)
+        {
+            return;
+        }
+
+        String name = enclosingMethodAttrInfo.getName(classFile);
+        String type = enclosingMethodAttrInfo.getType(classFile);
+
+        // See if we can find the method in the referenced class.
+        MethodInfo referencedMethodInfo = referencedClassFile.findMethod(name, type);
+
+        if (referencedMethodInfo == null)
+        {
+            // We couldn't find the enclosing method.
+            if (warn)
+            {
+                warningCount++;
+                System.err.println("Warning: " +
+                                   ClassUtil.externalClassName(classFile.getName()) +
+                                   ": can't find enclosing method '" +
+                                   ClassUtil.externalFullMethodDescription(className, 0, name, type) +
+                                   "' in class " +
+                                   ClassUtil.externalClassName(className));
+            }
+
+            return;
+        }
+
+        // Save the references.
+        enclosingMethodAttrInfo.referencedClassFile  = referencedClassFile;
+        enclosingMethodAttrInfo.referencedMethodInfo = referencedMethodInfo;
+    }
+
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        codeAttrInfo.instructionsAccept(classFile, methodInfo, classFileClassForNameReferenceInitializer);
+    }
+
+
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
+    {
+        signatureAttrInfo.referencedClassFiles =
+            findReferencedClasses(classFile.getCpString(signatureAttrInfo.u2signatureIndex));
+    }
+
+
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
+    {
+        // Initialize the annotations.
+        runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
+    {
+        // Initialize the annotations.
+        runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
+    {
+        // Initialize the annotations.
+        runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
+    {
+        // Initialize the annotations.
+        runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
+    {
+        // Initialize the annotation.
+        annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
+    }
+
+
+    // Implementations for LocalVariableInfoVisitor.
+
+    public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo)
+    {
+        localVariableInfo.referencedClassFile =
+            findClass(classFile.getCpString(localVariableInfo.u2descriptorIndex));
+    }
+
+
+    // Implementations for LocalVariableTypeInfoVisitor.
+
+    public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo)
+    {
+        localVariableTypeInfo.referencedClassFiles =
+            findReferencedClasses(classFile.getCpString(localVariableTypeInfo.u2signatureIndex));
+    }
+
+
+    // Implementations for AnnotationVisitor.
+
+    public void visitAnnotation(ClassFile classFile, Annotation annotation)
+    {
+        annotation.referencedClassFiles =
+            findReferencedClasses(classFile.getCpString(annotation.u2typeIndex));
+
+        // Initialize the element values.
+        annotation.elementValuesAccept(classFile, this);
+    }
+
+
+    // Implementations for ElementValueVisitor.
+
+    public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue)
+    {
+        initializeElementValue(classFile, annotation, constantElementValue);
+    }
+
+
+    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+    {
+        initializeElementValue(classFile, annotation, enumConstantElementValue);
+
+        enumConstantElementValue.referencedClassFiles =
+            findReferencedClasses(classFile.getCpString(enumConstantElementValue.u2typeNameIndex));
+    }
+
+
+    public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
+    {
+        initializeElementValue(classFile, annotation, classElementValue);
+
+        classElementValue.referencedClassFiles =
+            findReferencedClasses(classFile.getCpString(classElementValue.u2classInfoIndex));
+    }
+
+
+    public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue)
+    {
+        initializeElementValue(classFile, annotation, annotationElementValue);
+
+        // Initialize the annotation.
+        annotationElementValue.annotationAccept(classFile, this);
+    }
+
+
+    public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
+    {
+        initializeElementValue(classFile, annotation, arrayElementValue);
+
+        // Initialize the element values.
+        arrayElementValue.elementValuesAccept(classFile, annotation, this);
+    }
+
+
+    /**
+     * Initializes the referenced method of an element value, if any.
+     */
+    private void initializeElementValue(ClassFile classFile, Annotation annotation, ElementValue elementValue)
+    {
+        // See if we have a referenced class file.
+        if (annotation                      != null &&
+            annotation.referencedClassFiles != null &&
+            elementValue.u2elementName      != 0)
+        {
+            // See if we can find the method in the referenced class
+            // (ignoring the descriptor).
+            String name = classFile.getCpString(elementValue.u2elementName);
+            
+            elementValue.referencedMethodInfo =
+                annotation.referencedClassFiles[0].findMethod(name, null);
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns an array of class files referenced by the given descriptor, or
+     * <code>null</code> if there aren't any useful references.
+     */
+    private ClassFile[] findReferencedClasses(String descriptor)
+    {
+        DescriptorClassEnumeration enumeration =
+            new DescriptorClassEnumeration(descriptor);
+
+        int classCount = enumeration.classCount();
+        if (classCount > 0)
+        {
+            ClassFile[] referencedClassFiles = new ClassFile[classCount];
+
+            boolean foundReferencedClassFiles = false;
+
+            for (int index = 0; index < classCount; index++)
+            {
+                String name = enumeration.nextClassName();
+
+                ClassFile referencedClassFile = findClass(name);
+
+                if (referencedClassFile != null)
+                {
+                    referencedClassFiles[index] = referencedClassFile;
+                    foundReferencedClassFiles = true;
+                }
+            }
+
+            if (foundReferencedClassFiles)
+            {
+                return referencedClassFiles;
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Returns the class with the given name, either for the program class pool
+     * or from the library class pool, or <code>null</code> if it can't be found.
+     */
+    private ClassFile findClass(String name)
+    {
+        // First look for the class in the program class pool.
+        ClassFile classFile = programClassPool.getClass(name);
+
+        // Otherwise look for the class in the library class pool.
+        if (classFile == null)
+        {
+            classFile = libraryClassPool.getClass(name);
+        }
+
+        return classFile;
+    }
+}
diff --git a/src/proguard/classfile/util/ClassForNameChecker.java b/src/proguard/classfile/util/ClassForNameChecker.java
new file mode 100644
index 0000000..83885b3
--- /dev/null
+++ b/src/proguard/classfile/util/ClassForNameChecker.java
@@ -0,0 +1,225 @@
+/* $Id: ClassForNameChecker.java,v 1.11 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This class can check whether method references point to <code>Class.forName</code>
+ * or <code>.class</code>.
+ *
+ * @author Eric Lafortune
+ */
+class      ClassForNameChecker
+implements CpInfoVisitor,
+           MemberInfoVisitor,
+           AttrInfoVisitor,
+           InstructionVisitor
+{
+    // These fields acts as a return variables and status variables for the visitors.
+    private boolean isClassForNameInvocation;
+    private boolean insideDotClassMethod;
+    private boolean firstInstructionOk;
+
+
+    /**
+     * Creates a new ClassForNameChecker.
+     */
+    public ClassForNameChecker()
+    {
+    }
+
+
+    /**
+     * Checks whether the specified method reference points to
+     * <code>Class.forName</code> or <code>.class</code>.
+     * @param classFile            the class file in which the method reference
+     *                             is located.
+     * @param methodrefCpInfoIndex the index of the method reference in the
+     *                             constant pool.
+     * @return a boolean that indicates whether the reference points to either
+     *         method.
+     */
+    public boolean check(ClassFile classFile, int methodrefCpInfoIndex)
+    {
+        isClassForNameInvocation = false;
+
+        classFile.constantPoolEntryAccept(methodrefCpInfoIndex, this);
+
+        return isClassForNameInvocation;
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+
+    /**
+     * Checks whether the given method reference points to
+     * <code>Class.forName</code> or <code>.class</code>.
+     */
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    {
+        String className  = methodrefCpInfo.getClassName(classFile);
+        String methodName = methodrefCpInfo.getName(classFile);
+        String methodType = methodrefCpInfo.getType(classFile);
+
+        // Is it a reference to "Class Class.forName(String)"?
+        isClassForNameInvocation =
+            className .equals(ClassConstants.INTERNAL_CLASS_NAME_JAVA_LANG_CLASS) &&
+            methodName.equals(ClassConstants.INTERNAL_METHOD_NAME_CLASS_FOR_NAME) &&
+            methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_FOR_NAME);
+
+        if (isClassForNameInvocation || insideDotClassMethod)
+        {
+            return;
+        }
+
+        // Is it a reference to .class? This construct is typically implemented
+        // as (static) "Class class$(String)" or "Class class$(String, boolean)".
+        // First check the type, which has to be right to start with.
+        isClassForNameInvocation =
+            methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC) ||
+            methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES);
+
+        if (!isClassForNameInvocation)
+        {
+            return;
+        }
+
+        // Then check if the method perhaps still has its original "class$" name.
+        isClassForNameInvocation =
+            methodName.equals(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS);
+
+        if (isClassForNameInvocation)
+        {
+            return;
+        }
+
+        // We're still not sure it's not a reference to .class, since the
+        // method name may have been changed or obfuscated.
+        // Perform a more detailed analysis by looking at the referenced method
+        // itself. Make sure we don't do this recursively.
+        insideDotClassMethod = true;
+        methodrefCpInfo.referencedMemberInfoAccept(this);
+        insideDotClassMethod = false;
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+
+    /**
+     * Checks whether the given method is a .class implementation.
+     */
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        programMethodInfo.attributesAccept(programClassFile, this);
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
+
+
+    /**
+     * Checks whether the given code is an implementation of class$(String) or
+     * class$(String, boolean).
+     */
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        // Check whether the first instruction recalls the first argument of
+        // this method.
+        firstInstructionOk = false;
+        codeAttrInfo.instructionAccept(classFile, methodInfo, this, 0);
+
+        // Continue checking whether the second instruction invokes
+        // Class.forName.
+        if (firstInstructionOk)
+        {
+            codeAttrInfo.instructionAccept(classFile, methodInfo, this, 1);
+        }
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+
+
+    /**
+     * Checks whether this is a valid first instruction for a .class implementation.
+     */
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    {
+        firstInstructionOk = variableInstruction.opcode == InstructionConstants.OP_ALOAD_0;
+    }
+
+    /**
+     * Checks whether this is a valid second instruction for a .class implementation.
+     */
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    {
+        if (firstInstructionOk &&
+            cpInstruction.opcode == InstructionConstants.OP_INVOKESTATIC)
+        {
+            classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
+        }
+    }
+}
diff --git a/src/proguard/classfile/util/ClassNewInstanceChecker.java b/src/proguard/classfile/util/ClassNewInstanceChecker.java
new file mode 100644
index 0000000..d1ffe43
--- /dev/null
+++ b/src/proguard/classfile/util/ClassNewInstanceChecker.java
@@ -0,0 +1,97 @@
+/* $Id: ClassNewInstanceChecker.java,v 1.7 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This class can check whether method references point to
+ * <code>Class.newInstance</code>.
+ *
+ * @author Eric Lafortune
+ */
+class      ClassNewInstanceChecker
+implements CpInfoVisitor
+{
+    // This field acts as a return variable for the CpInfoVisitor.
+    private boolean isClassNewInstanceInvocation;
+
+
+    /**
+     * Creates a new ClassNewInstanceChecker.
+     */
+    public ClassNewInstanceChecker()
+    {
+    }
+
+
+    /**
+     * Checks whether the specified method reference points to
+     * <code>Class.newInstance</code>.
+     * @param classFile            the class file in which the method reference
+     *                             is located.
+     * @param methodrefCpInfoIndex the index of the method reference in the
+     *                             constant pool.
+     * @return a boolean that indicates whether the reference points to the
+     *         method.
+     */
+    public boolean check(ClassFile classFile, int methodrefCpInfoIndex)
+    {
+        isClassNewInstanceInvocation = false;
+
+        classFile.constantPoolEntryAccept(methodrefCpInfoIndex, this);
+
+        return isClassNewInstanceInvocation;
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+
+    /**
+     * Checks whether the given method reference points to
+     * <code>Class.newInstance</code>.
+     */
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    {
+        String className  = methodrefCpInfo.getClassName(classFile);
+        String methodName = methodrefCpInfo.getName(classFile);
+        String methodType = methodrefCpInfo.getType(classFile);
+
+        // Is it a reference to "Object Class.newInstance()"?
+        isClassNewInstanceInvocation =
+            className .equals(ClassConstants.INTERNAL_CLASS_NAME_JAVA_LANG_CLASS) &&
+            methodName.equals(ClassConstants.INTERNAL_METHOD_NAME_NEW_INSTANCE)   &&
+            methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_NEW_INSTANCE);
+    }
+}
diff --git a/src/proguard/classfile/util/ClassUtil.java b/src/proguard/classfile/util/ClassUtil.java
new file mode 100644
index 0000000..2367075
--- /dev/null
+++ b/src/proguard/classfile/util/ClassUtil.java
@@ -0,0 +1,935 @@
+/* $Id: ClassUtil.java,v 1.21 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+
+import java.io.*;
+import java.util.*;
+
+
+/**
+ * Utility methods for converting between internal and external representations
+ * of names and descriptions.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassUtil
+{
+    private static final String EMPTY_STRING = "";
+
+    private static final InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration();
+    private static final ExternalTypeEnumeration externalTypeEnumeration = new ExternalTypeEnumeration();
+
+
+    /**
+     * Checks whether the given class file magic number is correct.
+     * @param magicNumber the magic number.
+     * @throws IOException when the magic number is incorrect.
+     */
+    public static void checkMagicNumber(int magicNumber) throws IOException
+    {
+        if (magicNumber != ClassConstants.MAGIC)
+        {
+            throw new IOException("Invalid magic number ["+Integer.toHexString(magicNumber)+"] in class file");
+        }
+    }
+
+
+    /**
+     * Checks whether the given class file version numbers are supported.
+     * @param majorVersionNumber the major version number.
+     * @param minorVersionNumber the minor version number.
+     * @throws IOException when the version is not supported.
+     */
+    public static void checkVersionNumbers(int majorVersionNumber, int minorVersionNumber) throws IOException
+    {
+        if (majorVersionNumber < ClassConstants.MAJOR_VERSION_MIN ||
+            (majorVersionNumber == ClassConstants.MAJOR_VERSION_MIN &&
+             minorVersionNumber <  ClassConstants.MINOR_VERSION_MIN) ||
+            (majorVersionNumber == ClassConstants.MAJOR_VERSION_MAX &&
+             minorVersionNumber >  ClassConstants.MINOR_VERSION_MAX) ||
+            majorVersionNumber > ClassConstants.MAJOR_VERSION_MAX)
+        {
+            throw new IOException("Unsupported version number ["+majorVersionNumber+"."+minorVersionNumber+"] for class file format");
+        }
+    }
+
+
+    /**
+     * Converts an external class name into an internal class name.
+     * @param externalClassName the external class name,
+     *                          e.g. "<code>java.lang.Object</code>"
+     * @return the internal class name,
+     *                          e.g. "<code>java/lang/Object</code>".
+     */
+    public static String internalClassName(String externalClassName)
+    {
+        return externalClassName.replace(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR,
+                                         ClassConstants.INTERNAL_PACKAGE_SEPARATOR);
+    }
+
+
+    /**
+     * Converts an internal class description into an external class description.
+     * @param accessFlags       the access flags of the class.
+     * @param internalClassName the internal class name,
+     *                          e.g. "<code>java/lang/Object</code>".
+     * @return the external class description,
+     *                          e.g. "<code>public java.lang.Object</code>".
+     */
+    public static String externalFullClassDescription(int    accessFlags,
+                                                      String internalClassName)
+    {
+        return externalClassAccessFlags(accessFlags) +
+               externalClassName(internalClassName);
+    }
+
+
+    /**
+     * Converts an internal class name into an external class name.
+     * @param internalClassName the internal class name,
+     *                          e.g. "<code>java/lang/Object</code>".
+     * @return the external class name,
+     *                          e.g. "<code>java.lang.Object</code>".
+     */
+    public static String externalClassName(String internalClassName)
+    {
+        return //internalClassName.startsWith(ClassConstants.INTERNAL_PACKAGE_JAVA_LANG) &&
+               //internalClassName.indexOf(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, ClassConstants.INTERNAL_PACKAGE_JAVA_LANG.length() + 1) < 0 ?
+               //internalClassName.substring(ClassConstants.INTERNAL_PACKAGE_JAVA_LANG.length()) :
+               internalClassName.replace(ClassConstants.INTERNAL_PACKAGE_SEPARATOR,
+                                         ClassConstants.EXTERNAL_PACKAGE_SEPARATOR);
+    }
+
+
+    /**
+     * Converts an internal class name into an external short class name, without
+     * package specification.
+     * @param externalClassName the external class name,
+     *                          e.g. "<code>java.lang.Object</code>"
+     * @return the external short class name,
+     *                          e.g. "<code>Object</code>".
+     */
+    public static String externalShortClassName(String externalClassName)
+    {
+        int index = externalClassName.lastIndexOf(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR);
+        return externalClassName.substring(index+1);
+    }
+
+
+    /**
+     * Returns whether the given internal type is an array type.
+     * @param internalType the internal type,
+     *                     e.g. "<code>[[Ljava/lang/Object;</code>".
+     * @return <code>true</code> if the given type is an array type,
+     *         <code>false</code> otherwise.
+     */
+    public static boolean isInternalArrayType(String internalType)
+    {
+        return internalType.length() > 1 &&
+               internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_ARRAY;
+    }
+
+
+    /**
+     * Returns the number of dimensions of the given internal type.
+     * @param internalType the internal type,
+     *                     e.g. "<code>[[Ljava/lang/Object;</code>".
+     * @return the number of dimensions, e.g. 2.
+     */
+    public static int internalArrayTypeDimensionCount(String internalType)
+    {
+        int dimensions = 0;
+        while (internalType.charAt(dimensions) == ClassConstants.INTERNAL_TYPE_ARRAY)
+        {
+            dimensions++;
+        }
+
+        return dimensions;
+    }
+
+
+    /**
+     * Returns whether the given internal type is a plain primitive type
+     * (not void).
+     * @param internalType the internal type,
+     *                     e.g. "<code>I</code>".
+     * @return <code>true</code> if the given type is a class type,
+     *         <code>false</code> otherwise.
+     */
+    public static boolean isInternalPrimitiveType(char internalType)
+    {
+        return internalType == ClassConstants.INTERNAL_TYPE_BOOLEAN ||
+               internalType == ClassConstants.INTERNAL_TYPE_BYTE    ||
+               internalType == ClassConstants.INTERNAL_TYPE_CHAR    ||
+               internalType == ClassConstants.INTERNAL_TYPE_SHORT   ||
+               internalType == ClassConstants.INTERNAL_TYPE_INT     ||
+               internalType == ClassConstants.INTERNAL_TYPE_FLOAT   ||
+               internalType == ClassConstants.INTERNAL_TYPE_LONG    ||
+               internalType == ClassConstants.INTERNAL_TYPE_DOUBLE;
+    }
+
+
+    /**
+     * Returns whether the given internal type is a plain class type
+     * (not an array type).
+     * @param internalType the internal type,
+     *                     e.g. "<code>Ljava/lang/Object;</code>".
+     * @return <code>true</code> if the given type is a class type,
+     *         <code>false</code> otherwise.
+     */
+    public static boolean isInternalClassType(String internalType)
+    {
+        int length = internalType.length();
+        return length > 1 &&
+               internalType.charAt(0)        == ClassConstants.INTERNAL_TYPE_CLASS_START &&
+               internalType.charAt(length-1) == ClassConstants.INTERNAL_TYPE_CLASS_END;
+    }
+
+
+    /**
+     * Returns the internal element type of a given internal array type.
+     * @param internalArrayType the internal array type,
+     *                          e.g. "<code>[[Ljava/lang/Object;</code>" or
+     *                               "<code>[I</code>".
+     * @return the internal type of the array elements,
+     *                          e.g. "<code>Ljava/lang/Object;</code>" or
+     *                               "<code>I</code>".
+     */
+    public static String internalTypeFromArrayType(String internalArrayType)
+    {
+        int index = internalArrayType.lastIndexOf(ClassConstants.INTERNAL_TYPE_ARRAY);
+        return internalArrayType.substring(index+1);
+    }
+
+
+    /**
+     * Returns the internal class name of a given internal class type.
+     * @param internalClassType the internal class type,
+     *                          e.g. "<code>Ljava/lang/Object;</code>".
+     * @return the internal class name,
+     *                          e.g. "<code>java/lang/Object</code>".
+     */
+    public static String internalClassNameFromClassType(String internalClassType)
+    {
+        return internalClassType.substring(1, internalClassType.length()-1);
+    }
+
+
+    /**
+     * Returns the internal class name of any given internal type.
+     * The returned class name for primitive array types is
+     * "<code>java/lang/Object</code>".
+     * @param internalClassType the internal class type,
+     *                          e.g. "<code>Ljava/lang/Object;</code>" or
+     *                               "<code>[[I</code>".
+     * @return the internal class name,
+     *                          e.g. "<code>java/lang/Object</code>".
+     */
+    public static String internalClassNameFromType(String internalClassType)
+    {
+        // Is it an array type?
+        if (isInternalArrayType(internalClassType))
+        {
+            internalClassType = internalTypeFromArrayType(internalClassType);
+
+            // Is the array of a non-primitive type?
+            if (isInternalClassType(internalClassType))
+            {
+                internalClassType = internalClassNameFromClassType(internalClassType);
+            }
+            else
+            {
+                internalClassType = ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT;
+            }
+        }
+
+        return internalClassType;
+    }
+
+
+    /**
+     * Returns the internal type of the given internal method descriptor.
+     * @param internalMethodDescriptor the internal method descriptor,
+     *                                 e.g. "<code>(II)Z</code>".
+     * @return the internal return type,
+     *                                 e.g. "<code>Z</code>".
+     */
+    public static String internalMethodReturnType(String internalMethodDescriptor)
+    {
+        int index = internalMethodDescriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
+        return internalMethodDescriptor.substring(index + 1);
+    }
+
+
+    /**
+     * Returns the number of parameters of the given internal method descriptor.
+     * @param internalMethodDescriptor the internal method descriptor,
+     *                                 e.g. "<code>(ID)Z</code>".
+     * @return the number of parameters,
+     *                                 e.g. 2.
+     */
+    public static int internalMethodParameterCount(String internalMethodDescriptor)
+    {
+        internalTypeEnumeration.setDescriptor(internalMethodDescriptor);
+
+        int counter = 0;
+        while (internalTypeEnumeration.hasMoreTypes())
+        {
+            internalTypeEnumeration.nextType();
+
+            counter++;
+        }
+
+        return counter;
+    }
+
+
+    /**
+     * Returns the size taken up on the stack by the parameters of the given
+     * internal method descriptor. This accounts for long and double parameters
+     * taking up two spaces.
+     * @param internalMethodDescriptor the internal method descriptor,
+     *                                 e.g. "<code>(ID)Z</code>".
+     * @return the size taken up on the stack,
+     *                                 e.g. 3.
+     */
+    public static int internalMethodParameterSize(String internalMethodDescriptor)
+    {
+        internalTypeEnumeration.setDescriptor(internalMethodDescriptor);
+
+        int size = 0;
+        while (internalTypeEnumeration.hasMoreTypes())
+        {
+            String internalType = internalTypeEnumeration.nextType();
+
+            size += internalTypeSize(internalType);
+        }
+
+        return size;
+    }
+
+
+    /**
+     * Returns the size taken up on the stack by the given internal type.
+     * The size is 1, except for long and double types, for which it is 2,
+     * and for the void type, for which 0 is returned
+     * @param internalType the internal type,
+     *                     e.g. "<code>I</code>".
+     * @return the size taken up on the stack,
+     *                     e.g. 1.
+     */
+    public static int internalTypeSize(String internalType)
+    {
+        if (internalType.length() == 1)
+        {
+            char internalPrimitiveType = internalType.charAt(0);
+            if      (internalPrimitiveType == ClassConstants.INTERNAL_TYPE_LONG ||
+                     internalPrimitiveType == ClassConstants.INTERNAL_TYPE_DOUBLE)
+            {
+                return 2;
+            }
+            else if (internalPrimitiveType == ClassConstants.INTERNAL_TYPE_VOID)
+            {
+                return 0;
+            }
+        }
+
+        return 1;
+    }
+
+
+    /**
+     * Converts an external type into an internal type.
+     * @param externalType the external type,
+     *                     e.g. "<code>java.lang.Object[][]</code>" or
+     *                          "<code>int[]</code>".
+     * @return the internal type,
+     *                     e.g. "<code>[[Ljava/lang/Object;</code>" or
+     *                          "<code>[I</code>".
+     */
+    public static String internalType(String externalType)
+    {
+        // Strip the array part, if any.
+        int dimensionCount = externalArrayTypeDimensionCount(externalType);
+        if (dimensionCount > 0)
+        {
+            externalType = externalType.substring(0, externalType.length() - dimensionCount * ClassConstants.EXTERNAL_TYPE_ARRAY.length());
+        }
+
+        // Analyze the actual type part.
+        char internalTypeChar =
+            externalType.equals(ClassConstants.EXTERNAL_TYPE_VOID   ) ?
+                                ClassConstants.INTERNAL_TYPE_VOID     :
+            externalType.equals(ClassConstants.EXTERNAL_TYPE_BOOLEAN) ?
+                                ClassConstants.INTERNAL_TYPE_BOOLEAN  :
+            externalType.equals(ClassConstants.EXTERNAL_TYPE_BYTE   ) ?
+                                ClassConstants.INTERNAL_TYPE_BYTE     :
+            externalType.equals(ClassConstants.EXTERNAL_TYPE_CHAR   ) ?
+                                ClassConstants.INTERNAL_TYPE_CHAR     :
+            externalType.equals(ClassConstants.EXTERNAL_TYPE_SHORT  ) ?
+                                ClassConstants.INTERNAL_TYPE_SHORT    :
+            externalType.equals(ClassConstants.EXTERNAL_TYPE_INT    ) ?
+                                ClassConstants.INTERNAL_TYPE_INT      :
+            externalType.equals(ClassConstants.EXTERNAL_TYPE_FLOAT  ) ?
+                                ClassConstants.INTERNAL_TYPE_FLOAT    :
+            externalType.equals(ClassConstants.EXTERNAL_TYPE_LONG   ) ?
+                                ClassConstants.INTERNAL_TYPE_LONG     :
+            externalType.equals(ClassConstants.EXTERNAL_TYPE_DOUBLE ) ?
+                                ClassConstants.INTERNAL_TYPE_DOUBLE   :
+            externalType.equals("%"                                 ) ?
+                                '%'                                   :
+                                (char)0;
+
+        String internalType =
+            internalTypeChar != 0 ? ("" + internalTypeChar) :
+                                    (ClassConstants.INTERNAL_TYPE_CLASS_START +
+                                     internalClassName(externalType) +
+                                     ClassConstants.INTERNAL_TYPE_CLASS_END);
+
+        // Prepend the array part, if any.
+        for (int count = 0; count < dimensionCount; count++)
+        {
+            internalType = ClassConstants.INTERNAL_TYPE_ARRAY + internalType;
+        }
+
+        return internalType;
+    }
+
+
+    /**
+     * Returns the number of dimensions of the given external type.
+     * @param externalType the external type,
+     *                     e.g. "<code>[[Ljava/lang/Object;</code>".
+     * @return the number of dimensions, e.g. 2.
+     */
+    public static int externalArrayTypeDimensionCount(String externalType)
+    {
+        int dimensions = 0;
+        int length = ClassConstants.EXTERNAL_TYPE_ARRAY.length();
+        int offset = externalType.length() - length;
+        while (externalType.regionMatches(offset,
+                                          ClassConstants.EXTERNAL_TYPE_ARRAY,
+                                          0,
+                                          length))
+        {
+            dimensions++;
+            offset -= length;
+        }
+
+        return dimensions;
+    }
+
+
+    /**
+     * Converts an internal type into an external type.
+     * @param internalType the internal type,
+     *                     e.g. "<code>[[Ljava/lang/Object;</code>" or
+     *                          "<code>[I</code>".
+     * @return the external type,
+     *                     e.g. "<code>java.lang.Object[][]</code>" or
+     *                          "<code>int[]</code>".
+     */
+    public static String externalType(String internalType)
+    {
+        // Strip the array part, if any.
+        int dimensionCount = internalArrayTypeDimensionCount(internalType);
+        if (dimensionCount > 0)
+        {
+            internalType = internalType.substring(dimensionCount);
+        }
+
+        // Analyze the actual type part.
+        char internalTypeChar = internalType.charAt(0);
+
+        String externalType =
+            internalTypeChar == ClassConstants.INTERNAL_TYPE_VOID        ?
+                                ClassConstants.EXTERNAL_TYPE_VOID        :
+            internalTypeChar == ClassConstants.INTERNAL_TYPE_BOOLEAN     ?
+                                ClassConstants.EXTERNAL_TYPE_BOOLEAN     :
+            internalTypeChar == ClassConstants.INTERNAL_TYPE_BYTE        ?
+                                ClassConstants.EXTERNAL_TYPE_BYTE        :
+            internalTypeChar == ClassConstants.INTERNAL_TYPE_CHAR        ?
+                                ClassConstants.EXTERNAL_TYPE_CHAR        :
+            internalTypeChar == ClassConstants.INTERNAL_TYPE_SHORT       ?
+                                ClassConstants.EXTERNAL_TYPE_SHORT       :
+            internalTypeChar == ClassConstants.INTERNAL_TYPE_INT         ?
+                                ClassConstants.EXTERNAL_TYPE_INT         :
+            internalTypeChar == ClassConstants.INTERNAL_TYPE_FLOAT       ?
+                                ClassConstants.EXTERNAL_TYPE_FLOAT       :
+            internalTypeChar == ClassConstants.INTERNAL_TYPE_LONG        ?
+                                ClassConstants.EXTERNAL_TYPE_LONG        :
+            internalTypeChar == ClassConstants.INTERNAL_TYPE_DOUBLE      ?
+                                ClassConstants.EXTERNAL_TYPE_DOUBLE      :
+            internalTypeChar == ClassConstants.INTERNAL_TYPE_CLASS_START ?
+                                externalClassName(internalType.substring(1, internalType.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_END))) :
+                                null;
+
+        if (externalType == null)
+        {
+            throw new IllegalArgumentException("Unknown type ["+internalType+"]");
+        }
+
+        // Append the array part, if any.
+        for (int count = 0; count < dimensionCount; count++)
+        {
+            externalType = externalType + ClassConstants.EXTERNAL_TYPE_ARRAY;
+        }
+
+        return externalType;
+    }
+
+
+    /**
+     * Returns whether the given internal descriptor String represents a method
+     * descriptor.
+     * @param internalDescriptor the internal descriptor String,
+     *                           e.g. "<code>(II)Z</code>".
+     * @return <code>true</code> if the given String is a method descriptor,
+     *         <code>false</code> otherwise.
+     */
+    public static boolean isInternalMethodDescriptor(String internalDescriptor)
+    {
+        return internalDescriptor.charAt(0) == ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN;
+    }
+
+
+    /**
+     * Returns whether the given member String represents an external method
+     * name with arguments.
+     * @param externalMemberNameAndArguments the external member String,
+     *                                       e.g. "<code>myField</code>" or
+     *                                       e.g. "<code>myMethod(int,int)</code>".
+     * @return <code>true</code> if the given String refers to a method,
+     *         <code>false</code> otherwise.
+     */
+    public static boolean isExternalMethodNameAndArguments(String externalMemberNameAndArguments)
+    {
+        return externalMemberNameAndArguments.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN) > 0;
+    }
+
+
+    /**
+     * Returns the name part of the given external method name and arguments.
+     * @param externalMethodNameAndArguments the external method name and arguments,
+     *                                       e.g. "<code>myMethod(int,int)</code>".
+     * @return the name part of the String, e.g. "<code>myMethod</code>".
+     */
+    public static String externalMethodName(String externalMethodNameAndArguments)
+    {
+        externalTypeEnumeration.setDescriptor(externalMethodNameAndArguments);
+        return externalTypeEnumeration.methodName();
+    }
+
+
+    /**
+     * Converts the given external method return type and name and arguments to
+     * an internal method descriptor.
+     * @param externalReturnType             the external method return type,
+     *                                       e.g. "<code>boolean</code>".
+     * @param externalMethodNameAndArguments the external method name and arguments,
+     *                                       e.g. "<code>myMethod(int,int)</code>".
+     * @return the internal method descriptor,
+     *                                       e.g. "<code>(II)Z</code>".
+     */
+    public static String internalMethodDescriptor(String externalReturnType,
+                                                  String externalMethodNameAndArguments)
+    {
+        StringBuffer internalMethodDescriptor = new StringBuffer();
+        internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN);
+
+        externalTypeEnumeration.setDescriptor(externalMethodNameAndArguments);
+        while (externalTypeEnumeration.hasMoreTypes())
+        {
+            internalMethodDescriptor.append(internalType(externalTypeEnumeration.nextType()));
+        }
+
+        internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
+        internalMethodDescriptor.append(internalType(externalReturnType));
+
+        return internalMethodDescriptor.toString();
+    }
+
+
+    /**
+     * Converts the given external method return type and List of arguments to
+     * an internal method descriptor.
+     * @param externalReturnType the external method return type,
+     *                                       e.g. "<code>boolean</code>".
+     * @param externalArguments the external method arguments,
+     *                                       e.g. <code>{ "int", "int" }</code>.
+     * @return the internal method descriptor,
+     *                                       e.g. "<code>(II)Z</code>".
+     */
+    public static String internalMethodDescriptor(String externalReturnType,
+                                                  List   externalArguments)
+    {
+        StringBuffer internalMethodDescriptor = new StringBuffer();
+        internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN);
+
+        for (int index = 0; index < externalArguments.size(); index++)
+        {
+            internalMethodDescriptor.append(internalType((String)externalArguments.get(index)));
+        }
+
+        internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
+        internalMethodDescriptor.append(internalType(externalReturnType));
+
+        return internalMethodDescriptor.toString();
+    }
+
+
+    /**
+     * Converts an internal field description into an external full field description.
+     * @param accessFlags             the access flags of the field.
+     * @param fieldName               the field name,
+     *                                e.g. "<code>myField</code>".
+     * @param internalFieldDescriptor the internal field descriptor,
+     *                                e.g. "<code>Z</code>".
+     * @return the external full field description,
+     *                                e.g. "<code>public boolean myField</code>".
+     */
+    public static String externalFullFieldDescription(int    accessFlags,
+                                                      String fieldName,
+                                                      String internalFieldDescriptor)
+    {
+        return externalFieldAccessFlags(accessFlags) +
+               externalType(internalFieldDescriptor) +
+               " " +
+               fieldName;
+    }
+
+
+    /**
+     * Converts an internal method description into an external full method description.
+     * @param internalClassName        the internal name of the class of the method,
+     *                                 e.g. "<code>mypackage/MyClass</code>".
+     * @param accessFlags              the access flags of the method.
+     * @param internalMethodName       the internal method name,
+     *                                 e.g. "<code>myMethod</code>" or
+     *                                      "<code><init></code>".
+     * @param internalMethodDescriptor the internal method descriptor,
+     *                                 e.g. "<code>(II)Z</code>".
+     * @return the external full method description,
+     *                                 e.g. "<code>public boolean myMethod(int,int)</code>" or
+     *                                      "<code>public MyClass(int,int)</code>".
+     */
+    public static String externalFullMethodDescription(String internalClassName,
+                                                       int    accessFlags,
+                                                       String internalMethodName,
+                                                       String internalMethodDescriptor)
+    {
+        return externalMethodAccessFlags(accessFlags) +
+               externalMethodReturnTypeAndName(internalClassName,
+                                               internalMethodName,
+                                               internalMethodDescriptor) +
+               ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN +
+               externalMethodArguments(internalMethodDescriptor) +
+               ClassConstants.EXTERNAL_METHOD_ARGUMENTS_CLOSE;
+    }
+
+
+    /**
+     * Converts internal class access flags into an external access description.
+     * @param accessFlags the class access flags.
+     * @return the external class access description,
+     *         e.g. "<code>public final </code>".
+     */
+    public static String externalClassAccessFlags(int accessFlags)
+    {
+        return externalClassAccessFlags(accessFlags, "");
+    }
+
+
+    /**
+     * Converts internal class access flags into an external access description.
+     * @param accessFlags the class access flags.
+     * @param prefix      a prefix that is added to each access modifier.
+     * @return the external class access description,
+     *         e.g. "<code>public final </code>".
+     */
+    public static String externalClassAccessFlags(int accessFlags, String prefix)
+    {
+        if (accessFlags == 0)
+        {
+            return EMPTY_STRING;
+        }
+
+        StringBuffer string = new StringBuffer(50);
+
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(" ");
+        }
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(" ");
+        }
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_INTERFACE) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_INTERFACE).append(" ");
+        }
+        else
+        {
+            if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0)
+            {
+                string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ABSTRACT).append(" ");
+            }
+        }
+
+        return string.toString();
+    }
+
+
+    /**
+     * Converts internal field access flags into an external access description.
+     * @param accessFlags the field access flags.
+     * @return the external field access description,
+     *         e.g. "<code>public volatile </code>".
+     */
+    public static String externalFieldAccessFlags(int accessFlags)
+    {
+        return externalFieldAccessFlags(accessFlags, "");
+    }
+
+
+    /**
+     * Converts internal field access flags into an external access description.
+     * @param accessFlags the field access flags.
+     * @param prefix      a prefix that is added to each access modifier.
+     * @return the external field access description,
+     *         e.g. "<code>public volatile </code>".
+     */
+    public static String externalFieldAccessFlags(int accessFlags, String prefix)
+    {
+        if (accessFlags == 0)
+        {
+            return EMPTY_STRING;
+        }
+
+        StringBuffer string = new StringBuffer(50);
+
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(" ");
+        }
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PRIVATE).append(" ");
+        }
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PROTECTED).append(" ");
+        }
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STATIC).append(" ");
+        }
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(" ");
+        }
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_VOLATILE) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_VOLATILE).append(" ");
+        }
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_TRANSIENT) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_TRANSIENT).append(" ");
+        }
+
+        return string.toString();
+    }
+
+
+    /**
+     * Converts internal method access flags into an external access description.
+     * @param accessFlags the method access flags.
+     * @return the external method access description,
+     *                    e.g. "<code>public synchronized </code>".
+     */
+    public static String externalMethodAccessFlags(int accessFlags)
+    {
+        return externalMethodAccessFlags(accessFlags, "");
+    }
+
+
+    /**
+     * Converts internal method access flags into an external access description.
+     * @param accessFlags the method access flags.
+     * @param prefix      a prefix that is added to each access modifier.
+     * @return the external method access description,
+     *                    e.g. "public synchronized ".
+     */
+    public static String externalMethodAccessFlags(int accessFlags, String prefix)
+    {
+        if (accessFlags == 0)
+        {
+            return EMPTY_STRING;
+        }
+
+        StringBuffer string = new StringBuffer(50);
+
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(" ");
+        }
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PRIVATE).append(" ");
+        }
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PROTECTED).append(" ");
+        }
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STATIC).append(" ");
+        }
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(" ");
+        }
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_SYNCHRONIZED) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_SYNCHRONIZED).append(" ");
+        }
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_NATIVE) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_NATIVE).append(" ");
+        }
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ABSTRACT).append(" ");
+        }
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_STRICT) != 0)
+        {
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STRICT).append(" ");
+        }
+
+        return string.toString();
+    }
+
+
+    /**
+     * Converts an internal method descriptor into an external method return type.
+     * @param internalMethodDescriptor the internal method descriptor,
+     *                                 e.g. "<code>(II)Z</code>".
+     * @return the external method return type,
+     *                                 e.g. "<code>boolean</code>".
+     */
+    public static String externalMethodReturnType(String internalMethodDescriptor)
+    {
+        return externalType(internalMethodReturnType(internalMethodDescriptor));
+    }
+
+
+    /**
+     * Converts an internal class name, method name, and method descriptor to
+     * an external method return type and name.
+     * @param internalClassName        the internal name of the class of the method,
+     *                                 e.g. "<code>mypackage/MyClass</code>".
+     * @param internalMethodName       the internal method name,
+     *                                 e.g. "<code>myMethod</code>" or
+     *                                      "<code><init></code>".
+     * @param internalMethodDescriptor the internal method descriptor,
+     *                                 e.g. "<code>(II)Z</code>".
+     * @return the external method return type and name,
+     *                                 e.g. "<code>boolean myMethod</code>" or
+     *                                      "<code>MyClass</code>".
+     */
+    private static String externalMethodReturnTypeAndName(String internalClassName,
+                                                          String internalMethodName,
+                                                          String internalMethodDescriptor)
+    {
+        return internalMethodName.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ?
+                    (ClassUtil.externalShortClassName(
+                     ClassUtil.externalClassName(internalClassName))) :
+                    (ClassUtil.externalMethodReturnType(internalMethodDescriptor) +
+                     " " +
+                     internalMethodName);
+    }
+
+
+    /**
+     * Converts an internal method descriptor into an external method argument
+     * description.
+     * @param internalMethodDescriptor the internal method descriptor,
+     *                                 e.g. "<code>(II)Z</code>".
+     * @return the external method argument description,
+     *                                 e.g. "<code>int,int</code>".
+     */
+    public static String externalMethodArguments(String internalMethodDescriptor)
+    {
+        StringBuffer externalMethodNameAndArguments = new StringBuffer();
+
+        internalTypeEnumeration.setDescriptor(internalMethodDescriptor);
+        while (internalTypeEnumeration.hasMoreTypes())
+        {
+            externalMethodNameAndArguments.append(externalType(internalTypeEnumeration.nextType()));
+            if (internalTypeEnumeration.hasMoreTypes())
+            {
+                externalMethodNameAndArguments.append(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_SEPARATOR);
+            }
+        }
+
+        return externalMethodNameAndArguments.toString();
+    }
+
+
+    /**
+     * Returns the internal package name of the given internal class name.
+     * @param internalClassName the internal class name,
+     *                          e.g. "<code>java/lang/Object</code>".
+     * @return the internal package name,
+     *                          e.g. "<code>java/lang</code>".
+     */
+    public static String internalPackageName(String internalClassName)
+    {
+        int lastSeparatorIndex = internalClassName.lastIndexOf(ClassConstants.INTERNAL_PACKAGE_SEPARATOR);
+        if (lastSeparatorIndex < 0)
+        {
+            lastSeparatorIndex = 0;
+        }
+
+        return internalClassName.substring(0, lastSeparatorIndex);
+    }
+
+
+    /**
+     * Returns the external package name of the given external class name.
+     * @param externalClassName the external class name,
+     *                          e.g. "<code>java.lang.Object</code>".
+     * @return the external package name,
+     *                          e.g. "<code>java.lang</code>".
+     */
+    public static String externalPackageName(String externalClassName)
+    {
+        int lastSeparatorIndex = externalClassName.lastIndexOf(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR);
+        if (lastSeparatorIndex < 0)
+        {
+            lastSeparatorIndex = 0;
+        }
+
+        return externalClassName.substring(0, lastSeparatorIndex);
+    }
+}
diff --git a/src/proguard/classfile/util/DescriptorClassEnumeration.java b/src/proguard/classfile/util/DescriptorClassEnumeration.java
new file mode 100644
index 0000000..b0055fd
--- /dev/null
+++ b/src/proguard/classfile/util/DescriptorClassEnumeration.java
@@ -0,0 +1,165 @@
+/* $Id: DescriptorClassEnumeration.java,v 1.10 2004/11/26 22:53:15 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+
+/**
+ * A <code>DescriptorClassEnumeration</code> provides an enumeration of all
+ * classes mentioned in a given descriptor string.
+ * <p>
+ * A <code>DescriptorClassEnumeration</code> object can be reused for processing
+ * different subsequent descriptors, by means of the <code>setDescriptor</code>
+ * method.
+ *
+ * @author Eric Lafortune
+ */
+public class DescriptorClassEnumeration
+{
+    private String descriptor;
+    private int    index;
+
+
+    public DescriptorClassEnumeration(String descriptor)
+    {
+        setDescriptor(descriptor);
+    }
+
+
+    DescriptorClassEnumeration()
+    {
+    }
+
+
+    void setDescriptor(String descriptor)
+    {
+        this.descriptor = descriptor;
+
+        reset();
+    }
+
+
+    public void reset()
+    {
+        index = 0;
+    }
+
+
+    /**
+     * Returns the number of classes contained in the descriptor. This
+     * is the number of class names that the enumeration will return.
+     */
+    public int classCount()
+    {
+        int count = 0;
+
+        while (nextClassNameStartIndex() >= 0)
+        {
+            count++;
+            
+            nextClassNameEndIndex();
+        }
+
+        index = 0;
+        
+        return count;
+    }
+
+
+    public boolean hasMoreClassNames()
+    {
+        return index >= 0 && nextClassNameStartIndex() >= 0;
+    }
+
+
+    public String nextFluff()
+    {
+        int fluffStartIndex = index;
+        int fluffEndIndex   = nextClassNameStartIndex() + 1;
+
+        // There may be fluff at the end of the descriptor.
+        if (fluffEndIndex == 0)
+        {
+            fluffEndIndex = descriptor.length();
+        }
+
+        return descriptor.substring(fluffStartIndex, fluffEndIndex);
+    }
+
+
+    public String nextClassName()
+    {
+        int classNameStartIndex = nextClassNameStartIndex() + 1;
+        int classNameEndIndex   = nextClassNameEndIndex();
+        
+        return descriptor.substring(classNameStartIndex, classNameEndIndex);
+    }
+    
+    
+    private int nextClassNameStartIndex()
+    {
+        index = descriptor.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_START, index);
+        
+        return index;
+    }
+    
+    
+    private int nextClassNameEndIndex()
+    {
+        while (++index < descriptor.length())
+        {
+            char c = descriptor.charAt(index);
+            if (c == ClassConstants.INTERNAL_TYPE_CLASS_END ||
+                c == ClassConstants.INTERNAL_TYPE_GENERIC_START)
+            {
+                return index;
+            }
+        }
+        
+        throw new IllegalArgumentException("Missing class name terminator in descriptor ["+descriptor+"]");
+    }
+
+
+
+
+    /**
+     * A main method for testing the class name enumeration.
+     */
+    public static void main(String[] args)
+    {
+        try
+        {
+            System.out.println("Descriptor ["+args[0]+"]");
+            DescriptorClassEnumeration enumeration = new DescriptorClassEnumeration(args[0]);
+            System.out.println("Class count = "+enumeration.classCount());
+            System.out.println("  Fluff: ["+enumeration.nextFluff()+"]");
+            while (enumeration.hasMoreClassNames())
+            {
+                System.out.println("  Name:  ["+enumeration.nextClassName()+"]");
+                System.out.println("  Fluff: ["+enumeration.nextFluff()+"]");
+            }
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/src/proguard/classfile/util/ExternalTypeEnumeration.java b/src/proguard/classfile/util/ExternalTypeEnumeration.java
new file mode 100644
index 0000000..c1648db
--- /dev/null
+++ b/src/proguard/classfile/util/ExternalTypeEnumeration.java
@@ -0,0 +1,106 @@
+/* $Id: ExternalTypeEnumeration.java,v 1.9 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+
+
+/**
+ * An <code>ExternalTypeEnumeration</code> provides an enumeration of all
+ * types listed in a given external descriptor string. The method name can
+ * be retrieved separately.
+ * <p>
+ * A <code>ExternalTypeEnumeration</code> object can be reused for processing
+ * different subsequent descriptors, by means of the <code>setDescriptor</code>
+ * method.
+ *
+ * @author Eric Lafortune
+ */
+public class ExternalTypeEnumeration
+{
+    private String descriptor;
+    private int    index;
+
+
+    public ExternalTypeEnumeration(String descriptor)
+    {
+        setDescriptor(descriptor);
+    }
+
+
+    ExternalTypeEnumeration()
+    {
+    }
+
+
+    void setDescriptor(String descriptor)
+    {
+        this.descriptor = descriptor;
+
+        reset();
+    }
+
+
+    public void reset()
+    {
+        index = descriptor.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN) + 1;
+
+        if (index < 1)
+        {
+            throw new IllegalArgumentException("Missing opening parenthesis in descriptor ["+descriptor+"]");
+        }
+    }
+
+
+    public boolean hasMoreTypes()
+    {
+        return index < descriptor.length() - 1;
+    }
+
+
+    public String nextType()
+    {
+        int startIndex = index;
+
+        // Find the next separating comma.
+        index = descriptor.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_SEPARATOR,
+                                   startIndex);
+
+        // Otherwise find the closing parenthesis.
+        if (index < 0)
+        {
+            index = descriptor.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_CLOSE,
+                                       startIndex);
+            if (index < 0)
+            {
+                throw new IllegalArgumentException("Missing closing parenthesis in descriptor ["+descriptor+"]");
+            }
+        }
+
+        return descriptor.substring(startIndex, index++).trim();
+    }
+
+
+    public String methodName()
+    {
+        return descriptor.substring(0, descriptor.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN)).trim();
+    }
+}
diff --git a/src/proguard/classfile/util/InternalTypeEnumeration.java b/src/proguard/classfile/util/InternalTypeEnumeration.java
new file mode 100644
index 0000000..f60b5b6
--- /dev/null
+++ b/src/proguard/classfile/util/InternalTypeEnumeration.java
@@ -0,0 +1,108 @@
+/* $Id: InternalTypeEnumeration.java,v 1.8 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+
+
+/**
+ * An <code>InternalTypeEnumeration</code> provides an enumeration of all
+ * types listed in a given internal descriptor string. The return type can
+ * retrieved separately.
+ * <p>
+ * A <code>InternalTypeEnumeration</code> object can be reused for processing
+ * different subsequent descriptors, by means of the <code>setDescriptor</code>
+ * method.
+ *
+ * @author Eric Lafortune
+ */
+public class InternalTypeEnumeration
+{
+    private String descriptor;
+    private int    index;
+
+
+    public InternalTypeEnumeration(String descriptor)
+    {
+        setDescriptor(descriptor);
+    }
+
+
+    public InternalTypeEnumeration()
+    {
+    }
+
+
+    void setDescriptor(String descriptor)
+    {
+        this.descriptor = descriptor;
+
+        reset();
+    }
+
+
+    public void reset()
+    {
+        index = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN) + 1;
+
+        if (index < 1)
+        {
+            throw new IllegalArgumentException("Missing opening parenthesis in descriptor ["+descriptor+"]");
+        }
+    }
+
+
+    public boolean hasMoreTypes()
+    {
+        return descriptor.charAt(index) != ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE;
+    }
+
+
+    public String nextType()
+    {
+        int startIndex = index;
+
+        // Include all leading array characters.
+        while (descriptor.charAt(index) == ClassConstants.INTERNAL_TYPE_ARRAY)
+        {
+            index++;
+        }
+
+        // Class types consist of an entire string.
+        if (descriptor.charAt(index) == ClassConstants.INTERNAL_TYPE_CLASS_START)
+        {
+            index = descriptor.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_END,
+                                       index + 1);
+            if (index < 0)
+            {
+                throw new IllegalArgumentException("Missing closing class type in descriptor ["+descriptor+"]");
+            }
+        }
+
+        return descriptor.substring(startIndex, ++index);
+    }
+
+
+    public String returnType()
+    {
+        return descriptor.substring(descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE) + 1);
+    }
+}
diff --git a/src/proguard/classfile/util/MemberFinder.java b/src/proguard/classfile/util/MemberFinder.java
new file mode 100644
index 0000000..27b6451
--- /dev/null
+++ b/src/proguard/classfile/util/MemberFinder.java
@@ -0,0 +1,236 @@
+/* $Id: MemberFinder.java,v 1.4 2004/12/18 20:22:42 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This class provides methods to find class members in a given class or in its
+ * hierarchy.
+ *
+ * @author Eric Lafortune
+ */
+public class MemberFinder
+  implements MemberInfoVisitor
+{
+    private static class MemberFoundException extends IllegalArgumentException {};
+    private static final MemberFoundException MEMBER_FOUND = new MemberFoundException();
+
+    private ClassFile  classFile;
+    private MemberInfo memberInfo;
+
+
+    /**
+     * Finds the field with the given name and descriptor in the given
+     * class file or its hierarchy.
+     */
+    public FieldInfo findField(ClassFile classFile, String name, String descriptor)
+    {
+        return (FieldInfo)findMember(classFile, name, descriptor, true);
+    }
+
+    
+    /**
+     * Finds the method with the given name and descriptor in the given
+     * class file or its hierarchy.
+     */
+    public MethodInfo findMethod(ClassFile classFile, String name, String descriptor)
+    {
+        return (MethodInfo)findMember(classFile, name, descriptor, false);
+    }
+    
+    
+    /**
+     * Finds the class member with the given name and descriptor in the given
+     * class file or its hierarchy.
+     */
+    public MemberInfo findMember(ClassFile classFile,
+                                 String    name,
+                                 String    descriptor,
+                                 boolean   isField)
+    {
+        // For efficiency, first see if we can find the method in the
+        // referenced class itself.
+        this.classFile  = classFile;
+        this.memberInfo = isField ?
+            (MemberInfo)classFile.findField(name, descriptor) :
+            (MemberInfo)classFile.findMethod(name, descriptor);
+
+        if (memberInfo == null)
+        {
+            // We didn't find the method yet. Organize a search in the hierarchy
+            // of superclasses and interfaces. This can happen with classes
+            // compiled with "-target 1.2" or higher (the default in JDK 1.4).
+            try
+            {
+                this.classFile  = null;
+                this.memberInfo = null;
+                classFile.hierarchyAccept(false, true, true, false,
+                                          isField ?
+                                              (ClassFileVisitor)new NamedFieldVisitor(name, descriptor,
+                                                                new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, this)) :
+                                              (ClassFileVisitor)new NamedMethodVisitor(name, descriptor,
+                                                                new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, this)));
+            }
+            catch (MemberFoundException ex)
+            {
+            }
+        }
+        
+        return memberInfo;
+    }
+
+    
+    /**
+     * Returns the corresponding class file of the most recently found class
+     * member.
+     */
+    public ClassFile correspondingClassFile()
+    {
+        return classFile;
+    }
+    
+    
+    /**
+     * Returns whether the given method is overridden anywhere down the class
+     * hierarchy.
+     */
+    public boolean isOverriden(ClassFile  classFile,
+                               MethodInfo methodInfo)
+    {
+        String name       = methodInfo.getName(classFile);
+        String descriptor = methodInfo.getDescriptor(classFile);
+
+        // Go looking for the method down the class hierarchy.
+        try
+        {
+            this.classFile  = null;
+            this.memberInfo = null;
+            classFile.hierarchyAccept(false, false, false, true,
+                                      new NamedMethodVisitor(name, descriptor,
+                                      new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, this)));
+        }
+        catch (MemberFoundException ex)
+        {
+            // We've found an overriding method.
+            return true;
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Returns whether the given field is shadowed anywhere down the class
+     * hierarchy.
+     */
+    public boolean isShadowed(ClassFile classFile,
+                              FieldInfo fieldInfo)
+    {
+        String name       = fieldInfo.getName(classFile);
+        String descriptor = fieldInfo.getDescriptor(classFile);
+
+        // Go looking for the method down the class hierarchy.
+        try
+        {
+            this.classFile  = null;
+            this.memberInfo = null;
+            classFile.hierarchyAccept(false, false, false, true,
+                                      new NamedFieldVisitor(name, descriptor,
+                                      new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, this)));
+        }
+        catch (MemberFoundException ex)
+        {
+            // We've found an overriding method.
+            return true;
+        }
+
+        return false;
+    }
+
+
+//    // Implementations for ClassFileVisitor.
+//
+//    public void visitProgramClassFile(ProgramClassFile programClassFile)
+//    {
+//        visitClassFile(programClassFile);
+//    }
+//    
+//    
+//    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+//    {
+//        visitClassFile(libraryClassFile);
+//    }
+//    
+//    
+//    private void visitClassFile(ClassFile classFile)
+//    {
+//        if (memberInfo == null)
+//        {
+//            memberInfo = isField ?
+//                (MemberInfo)classFile.findField(name, descriptor) :
+//                (MemberInfo)classFile.findMethod(name, descriptor);
+//                
+//            if (memberInfo != null)
+//            {
+//                this.classFile = classFile;
+//            }
+//        }
+//    }
+    
+    
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        visitMemberInfo(programClassFile, programFieldInfo);
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        visitMemberInfo(programClassFile, programMethodInfo);
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    {
+        visitMemberInfo(libraryClassFile, libraryFieldInfo);
+    }
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        visitMemberInfo(libraryClassFile, libraryMethodInfo);
+    }
+
+
+    private void visitMemberInfo(ClassFile classFile, MemberInfo memberInfo)
+    {
+        this.classFile  = classFile;
+        this.memberInfo = memberInfo;
+        
+        throw MEMBER_FOUND;
+    }
+}
diff --git a/src/proguard/classfile/util/package.html b/src/proguard/classfile/util/package.html
new file mode 100644
index 0000000..b1b881e
--- /dev/null
+++ b/src/proguard/classfile/util/package.html
@@ -0,0 +1,3 @@
+<body>
+This package contains utility classes for processing class files.
+</body>
diff --git a/src/proguard/classfile/visitor/AllClassFileVisitor.java b/src/proguard/classfile/visitor/AllClassFileVisitor.java
new file mode 100644
index 0000000..64e28f6
--- /dev/null
+++ b/src/proguard/classfile/visitor/AllClassFileVisitor.java
@@ -0,0 +1,47 @@
+/* $Id: AllClassFileVisitor.java,v 1.10 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This ClassPoolVisitor lets a given ClassFileVisitor visit all ClassFile
+ * objects of the class pools it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class AllClassFileVisitor implements ClassPoolVisitor
+{
+    private ClassFileVisitor classFileVisitor;
+
+
+    public AllClassFileVisitor(ClassFileVisitor classFileVisitor)
+    {
+        this.classFileVisitor = classFileVisitor;
+    }
+
+
+    public void visitClassPool(ClassPool classPool)
+    {
+        classPool.classFilesAccept(classFileVisitor);
+    }
+}
diff --git a/src/proguard/classfile/visitor/AllCpInfoVisitor.java b/src/proguard/classfile/visitor/AllCpInfoVisitor.java
new file mode 100644
index 0000000..d23a284
--- /dev/null
+++ b/src/proguard/classfile/visitor/AllCpInfoVisitor.java
@@ -0,0 +1,52 @@
+/* $Id: AllCpInfoVisitor.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This ClassFileVisitor lets a given CpInfoVisitor visit all constant pool
+ * entries of the program class files it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class AllCpInfoVisitor implements ClassFileVisitor
+{
+    private CpInfoVisitor cpInfoVisitor;
+
+
+    public AllCpInfoVisitor(CpInfoVisitor cpInfoVisitor)
+    {
+        this.cpInfoVisitor = cpInfoVisitor;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        programClassFile.constantPoolEntriesAccept(cpInfoVisitor);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile) {}
+}
diff --git a/src/proguard/classfile/visitor/AllFieldVisitor.java b/src/proguard/classfile/visitor/AllFieldVisitor.java
new file mode 100644
index 0000000..ac23349
--- /dev/null
+++ b/src/proguard/classfile/visitor/AllFieldVisitor.java
@@ -0,0 +1,55 @@
+/* $Id: AllFieldVisitor.java,v 1.12 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This ClassFileVisitor lets a given MemberInfoVisitor visit all FieldMemberInfo
+ * objects of the class files it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class AllFieldVisitor implements ClassFileVisitor
+{
+    private MemberInfoVisitor memberInfoVisitor;
+
+
+    public AllFieldVisitor(MemberInfoVisitor memberInfoVisitor)
+    {
+        this.memberInfoVisitor = memberInfoVisitor;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        programClassFile.fieldsAccept(memberInfoVisitor);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        libraryClassFile.fieldsAccept(memberInfoVisitor);
+    }
+}
diff --git a/src/proguard/classfile/visitor/AllMemberInfoVisitor.java b/src/proguard/classfile/visitor/AllMemberInfoVisitor.java
new file mode 100644
index 0000000..e24d744
--- /dev/null
+++ b/src/proguard/classfile/visitor/AllMemberInfoVisitor.java
@@ -0,0 +1,57 @@
+/* $Id: AllMemberInfoVisitor.java,v 1.10 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This ClassFileVisitor lets a given MemberInfoVisitor visit all MemberInfo
+ * objects of the class files it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class AllMemberInfoVisitor implements ClassFileVisitor
+{
+    private MemberInfoVisitor memberInfoVisitor;
+
+
+    public AllMemberInfoVisitor(MemberInfoVisitor memberInfoVisitor)
+    {
+        this.memberInfoVisitor = memberInfoVisitor;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        programClassFile.fieldsAccept(memberInfoVisitor);
+        programClassFile.methodsAccept(memberInfoVisitor);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        libraryClassFile.fieldsAccept(memberInfoVisitor);
+        libraryClassFile.methodsAccept(memberInfoVisitor);
+    }
+}
diff --git a/src/proguard/classfile/visitor/AllMethodVisitor.java b/src/proguard/classfile/visitor/AllMethodVisitor.java
new file mode 100644
index 0000000..e259ee4
--- /dev/null
+++ b/src/proguard/classfile/visitor/AllMethodVisitor.java
@@ -0,0 +1,55 @@
+/* $Id: AllMethodVisitor.java,v 1.12 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This ClassFileVisitor lets a given MemberInfoVisitor visit all MethodMemberInfo
+ * objects of the class files it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class AllMethodVisitor implements ClassFileVisitor
+{
+    private MemberInfoVisitor memberInfoVisitor;
+
+
+    public AllMethodVisitor(MemberInfoVisitor memberInfoVisitor)
+    {
+        this.memberInfoVisitor = memberInfoVisitor;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        programClassFile.methodsAccept(memberInfoVisitor);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        libraryClassFile.methodsAccept(memberInfoVisitor);
+    }
+}
diff --git a/src/proguard/classfile/visitor/BottomClassFileFilter.java b/src/proguard/classfile/visitor/BottomClassFileFilter.java
new file mode 100644
index 0000000..2af242a
--- /dev/null
+++ b/src/proguard/classfile/visitor/BottomClassFileFilter.java
@@ -0,0 +1,69 @@
+/* $Id: BottomClassFileFilter.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This <code>ClassFileVisitor</code> delegates its visits to another given
+ * <code>ClassFileVisitor</code>, but only when visiting class files that don't
+ * have any subclasses.
+ *
+ * @author Eric Lafortune
+ */
+public class BottomClassFileFilter implements ClassFileVisitor
+{
+    private ClassFileVisitor classFileVisitor;
+
+
+    /**
+     * Creates a new ProgramClassFileFilter.
+     * @param classFileVisitor the <code>ClassFileVisitor</code> to which visits
+     *                         will be delegated.
+     */
+    public BottomClassFileFilter(ClassFileVisitor classFileVisitor)
+    {
+        this.classFileVisitor = classFileVisitor;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Is this a bottom class in the class hierarchy?
+        if (programClassFile.subClasses == null)
+        {
+            classFileVisitor.visitProgramClassFile(programClassFile);
+        }
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        // Is this a bottom class in the class hierarchy?
+        if (libraryClassFile.subClasses == null)
+        {
+            classFileVisitor.visitLibraryClassFile(libraryClassFile);
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/ClassFileAccessFilter.java b/src/proguard/classfile/visitor/ClassFileAccessFilter.java
new file mode 100644
index 0000000..35848c1
--- /dev/null
+++ b/src/proguard/classfile/visitor/ClassFileAccessFilter.java
@@ -0,0 +1,88 @@
+/* $Id: ClassFileAccessFilter.java,v 1.6 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This <code>ClassFileVisitor</code> delegates its visits to another given
+ * <code>ClassFileVisitor</code>, but only when the visited class file
+ * has the proper access flags.
+ *
+ * @see ClassConstants
+ *
+ * @author Eric Lafortune
+ */
+public class ClassFileAccessFilter implements ClassFileVisitor
+{
+    private int              requiredSetAccessFlags;
+    private int              requiredUnsetAccessFlags;
+    private ClassFileVisitor classFileVisitor;
+
+
+    /**
+     * Creates a new ClassFileAccessFilter.
+     * @param requiredSetAccessFlags   the class access flags that should be
+     *                                 set.
+     * @param requiredUnsetAccessFlags the class access flags that should be
+     *                                 unset.
+     * @param classFileVisitor         the <code>ClassFileVisitor</code> to
+     *                                 which visits will be delegated.
+     */
+    public ClassFileAccessFilter(int              requiredSetAccessFlags,
+                                 int              requiredUnsetAccessFlags,
+                                 ClassFileVisitor classFileVisitor)
+    {
+        this.requiredSetAccessFlags   = requiredSetAccessFlags;
+        this.requiredUnsetAccessFlags = requiredUnsetAccessFlags;
+        this.classFileVisitor         = classFileVisitor;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        if (accepted(programClassFile.getAccessFlags()))
+        {
+            classFileVisitor.visitProgramClassFile(programClassFile);
+        }
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        if (accepted(libraryClassFile.getAccessFlags()))
+        {
+            classFileVisitor.visitLibraryClassFile(libraryClassFile);
+        }
+    }
+
+
+    // Small utility methods.
+
+    private boolean accepted(int accessFlags)
+    {
+        return (requiredSetAccessFlags   & ~accessFlags) == 0 &&
+               (requiredUnsetAccessFlags &  accessFlags) == 0;
+    }
+}
diff --git a/src/proguard/classfile/visitor/ClassFileCleaner.java b/src/proguard/classfile/visitor/ClassFileCleaner.java
new file mode 100644
index 0000000..a80348e
--- /dev/null
+++ b/src/proguard/classfile/visitor/ClassFileCleaner.java
@@ -0,0 +1,368 @@
+/* $Id: ClassFileCleaner.java,v 1.17 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+
+/**
+ * This <code>ClassFileVisitor</code> removes all visitor information of the
+ * class files it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassFileCleaner
+  implements ClassFileVisitor,
+             CpInfoVisitor,
+             MemberInfoVisitor,
+             AttrInfoVisitor,
+             ExceptionInfoVisitor,
+             InnerClassesInfoVisitor,
+             AnnotationVisitor,
+             ElementValueVisitor
+{
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        clean(programClassFile);
+
+        programClassFile.constantPoolEntriesAccept(this);
+
+        programClassFile.fieldsAccept(this);
+        programClassFile.methodsAccept(this);
+
+        programClassFile.attributesAccept(this);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        clean(libraryClassFile);
+
+        libraryClassFile.fieldsAccept(this);
+        libraryClassFile.methodsAccept(this);
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    {
+        clean(classCpInfo);
+    }
+
+
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo)
+    {
+        clean(doubleCpInfo);
+    }
+
+
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    {
+        clean(fieldrefCpInfo);
+    }
+
+
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo)
+    {
+        clean(floatCpInfo);
+    }
+
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo)
+    {
+        clean(integerCpInfo);
+    }
+
+
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    {
+        clean(interfaceMethodrefCpInfo);
+    }
+
+
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo)
+    {
+        clean(longCpInfo);
+    }
+
+
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    {
+        clean(methodrefCpInfo);
+    }
+
+
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo)
+    {
+        clean(nameAndTypeCpInfo);
+    }
+
+
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
+    {
+        clean(stringCpInfo);
+    }
+
+
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo)
+    {
+        clean(utf8CpInfo);
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        visitMemberInfo(programClassFile, programFieldInfo);
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        visitMemberInfo(programClassFile, programMethodInfo);
+    }
+
+
+    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    {
+        clean(programMemberInfo);
+
+        programMemberInfo.attributesAccept(programClassFile, this);
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    {
+        clean(libraryFieldInfo);
+    }
+
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        clean(libraryMethodInfo);
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo)
+    {
+        clean(unknownAttrInfo);
+    }
+
+
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
+    {
+        clean(innerClassesAttrInfo);
+
+        innerClassesAttrInfo.innerClassEntriesAccept(classFile, this);
+    }
+
+
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
+    {
+        clean(enclosingMethodAttrInfo);
+    }
+
+
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo)
+    {
+        clean(constantValueAttrInfo);
+    }
+
+
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo)
+    {
+        clean(exceptionsAttrInfo);
+
+        exceptionsAttrInfo.exceptionEntriesAccept((ProgramClassFile)classFile, this);
+    }
+
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        clean(codeAttrInfo);
+
+        codeAttrInfo.exceptionsAccept(classFile, methodInfo, this);
+        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
+    }
+
+
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo)
+    {
+        clean(lineNumberTableAttrInfo);
+    }
+
+
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
+    {
+        clean(localVariableTableAttrInfo);
+    }
+
+
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
+    {
+        clean(localVariableTypeTableAttrInfo);
+    }
+
+
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo)
+    {
+        clean(sourceFileAttrInfo);
+    }
+
+
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo)
+    {
+        clean(sourceDirAttrInfo);
+    }
+
+
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo)
+    {
+        clean(deprecatedAttrInfo);
+    }
+
+
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo)
+    {
+        clean(syntheticAttrInfo);
+    }
+
+
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
+    {
+        clean(signatureAttrInfo);
+    }
+
+
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
+    {
+        clean(runtimeVisibleAnnotationsAttrInfo);
+
+        runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
+    {
+        clean(runtimeInvisibleAnnotationsAttrInfo);
+
+        runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
+    {
+        clean(runtimeVisibleParameterAnnotationsAttrInfo);
+
+        runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
+    {
+        clean(runtimeInvisibleParameterAnnotationsAttrInfo);
+
+        runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
+    {
+        clean(annotationDefaultAttrInfo);
+
+        annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
+    }
+
+
+    // Implementations for InnerClassesInfoVisitor.
+
+    public void visitInnerClassesInfo(ClassFile classFile, InnerClassesInfo innerClassesInfo)
+    {
+        clean(innerClassesInfo);
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo)
+    {
+        clean(exceptionInfo);
+    }
+
+
+    // Implementations for AnnotationVisitor.
+
+    public void visitAnnotation(ClassFile classFile, Annotation annotation)
+    {
+        clean(annotation);
+
+        annotation.elementValuesAccept(classFile, this);
+    }
+
+
+    // Implementations for ElementValueVisitor.
+
+    public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue)
+    {
+        clean(constantElementValue);
+    }
+
+
+    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+    {
+        clean(enumConstantElementValue);
+    }
+
+
+    public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
+    {
+        clean(classElementValue);
+    }
+
+
+    public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue)
+    {
+        clean(annotationElementValue);
+
+        annotationElementValue.annotationAccept(classFile, this);
+    }
+
+
+    public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
+    {
+        clean(arrayElementValue);
+    }
+
+
+    // Small utility methods.
+
+    private void clean(VisitorAccepter visitorAccepter)
+    {
+        visitorAccepter.setVisitorInfo(null);
+    }
+}
diff --git a/src/proguard/classfile/visitor/ClassFileHierarchyTraveler.java b/src/proguard/classfile/visitor/ClassFileHierarchyTraveler.java
new file mode 100644
index 0000000..c263058
--- /dev/null
+++ b/src/proguard/classfile/visitor/ClassFileHierarchyTraveler.java
@@ -0,0 +1,91 @@
+/* $Id: ClassFileHierarchyTraveler.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This <code>ClassFileVisitor</code> lets a given <code>ClassFileVisitor</code>
+ * optionally travel to the visited class, its superclass, its interfaces, and
+ * its subclasses.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassFileHierarchyTraveler implements ClassFileVisitor
+{
+    private boolean visitThisClass;
+    private boolean visitSuperClass;
+    private boolean visitInterfaces;
+    private boolean visitSubclasses;
+
+    private ClassFileVisitor classFileVisitor;
+
+
+    /**
+     * Creates a new ClassFileHierarchyTraveler.
+     * @param visitThisClass   specifies whether to visit the originally visited
+     *                         classes.
+     * @param visitSuperClass  specifies whether to visit the super classes of
+     *                         the visited classes.
+     * @param visitInterfaces  specifies whether to visit the interfaces of
+     *                         the visited classes.
+     * @param visitSubclasses  specifies whether to visit the subclasses of
+     *                         the visited classes.
+     * @param classFileVisitor the <code>ClassFileVisitor</code> to
+     *                         which visits will be delegated.
+     */
+    public ClassFileHierarchyTraveler(boolean          visitThisClass,
+                                      boolean          visitSuperClass,
+                                      boolean          visitInterfaces,
+                                      boolean          visitSubclasses,
+                                      ClassFileVisitor classFileVisitor)
+    {
+        this.visitThisClass  = visitThisClass;
+        this.visitSuperClass = visitSuperClass;
+        this.visitInterfaces = visitInterfaces;
+        this.visitSubclasses = visitSubclasses;
+
+        this.classFileVisitor = classFileVisitor;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        programClassFile.hierarchyAccept(visitThisClass,
+                                         visitSuperClass,
+                                         visitInterfaces,
+                                         visitSubclasses,
+                                         classFileVisitor);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        libraryClassFile.hierarchyAccept(visitThisClass,
+                                         visitSuperClass,
+                                         visitInterfaces,
+                                         visitSubclasses,
+                                         classFileVisitor);
+    }
+}
diff --git a/src/proguard/classfile/visitor/ClassFileMemberInfoVisitor.java b/src/proguard/classfile/visitor/ClassFileMemberInfoVisitor.java
new file mode 100644
index 0000000..19fd93a
--- /dev/null
+++ b/src/proguard/classfile/visitor/ClassFileMemberInfoVisitor.java
@@ -0,0 +1,88 @@
+/* $Id: ClassFileMemberInfoVisitor.java,v 1.4 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This MemberInfoVisitor delegates all visits to a given ClassFileVisitor.
+ * The latter visits the class file of each visited class member, although
+ * never twice in a row.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassFileMemberInfoVisitor implements MemberInfoVisitor
+{
+    private ClassFileVisitor classFileVisitor;
+
+    private ClassFile lastVisitedClassFile;
+
+
+    public ClassFileMemberInfoVisitor(ClassFileVisitor classFileVisitor)
+    {
+        this.classFileVisitor = classFileVisitor;
+    }
+
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        if (!programClassFile.equals(lastVisitedClassFile))
+        {
+            classFileVisitor.visitProgramClassFile(programClassFile);
+
+            lastVisitedClassFile = programClassFile;
+        }
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        if (!programClassFile.equals(lastVisitedClassFile))
+        {
+            classFileVisitor.visitProgramClassFile(programClassFile);
+
+            lastVisitedClassFile = programClassFile;
+        }
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    {
+        if (!libraryClassFile.equals(lastVisitedClassFile))
+        {
+            classFileVisitor.visitLibraryClassFile(libraryClassFile);
+
+            lastVisitedClassFile = libraryClassFile;
+        }
+    }
+
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        if (!libraryClassFile.equals(lastVisitedClassFile))
+        {
+            classFileVisitor.visitLibraryClassFile(libraryClassFile);
+
+            lastVisitedClassFile = libraryClassFile;
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/ClassFileNameFilter.java b/src/proguard/classfile/visitor/ClassFileNameFilter.java
new file mode 100644
index 0000000..96e8e17
--- /dev/null
+++ b/src/proguard/classfile/visitor/ClassFileNameFilter.java
@@ -0,0 +1,81 @@
+/* $Id: ClassFileNameFilter.java,v 1.8 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.util.*;
+
+
+/**
+ * This <code>ClassFileVisitor</code> delegates its visits to another given
+ * <code>ClassFileVisitor</code>, but only when the visited class file
+ * has a name that matches a given regular expression.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassFileNameFilter implements ClassFileVisitor
+{
+    private ClassFileVisitor         classFileVisitor;
+    private StringMatcher regularExpressionMatcher;
+
+
+    /**
+     * Creates a new ClassFileNameFilter.
+     * @param classFileVisitor  the <code>ClassFileVisitor</code> to which visits
+     *                          will be delegated.
+     * @param regularExpression the regular expression against which class names
+     *                          will be matched.
+     */
+    public ClassFileNameFilter(ClassFileVisitor classFileVisitor,
+                               String           regularExpression)
+    {
+        this.classFileVisitor         = classFileVisitor;
+        this.regularExpressionMatcher = new ClassNameMatcher(regularExpression);
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        if (accepted(programClassFile.getName()))
+        {
+            classFileVisitor.visitProgramClassFile(programClassFile);
+        }
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        if (accepted(libraryClassFile.getName()))
+        {
+            classFileVisitor.visitLibraryClassFile(libraryClassFile);
+        }
+    }
+
+
+    // Small utility methods.
+
+    private boolean accepted(String name)
+    {
+        return regularExpressionMatcher.matches(name);
+    }
+}
diff --git a/src/proguard/classfile/visitor/ClassFilePrinter.java b/src/proguard/classfile/visitor/ClassFilePrinter.java
new file mode 100644
index 0000000..8d1268b
--- /dev/null
+++ b/src/proguard/classfile/visitor/ClassFilePrinter.java
@@ -0,0 +1,677 @@
+/* $Id: ClassFilePrinter.java,v 1.30 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.util.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.instruction.*;
+
+import java.io.*;
+
+
+/**
+ * This <code>ClassFileVisitor</code> prints out the complete internal
+ * structure of the class files it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassFilePrinter
+  implements ClassFileVisitor,
+             CpInfoVisitor,
+             MemberInfoVisitor,
+             AttrInfoVisitor,
+             ExceptionInfoVisitor,
+             InnerClassesInfoVisitor,
+             AnnotationVisitor,
+             ElementValueVisitor,
+             InstructionVisitor
+{
+    private static final String INDENTATION = "  ";
+
+    private PrintStream ps;
+    private int         indentation;
+
+
+    /**
+     * Creates a new ClassFilePrinter that prints to <code>System.out</code>.
+     */
+    public ClassFilePrinter()
+    {
+        this(System.out);
+    }
+
+
+    /**
+     * Creates a new ClassFilePrinter that prints to the given
+     * <code>PrintStream</code>.
+     */
+    public ClassFilePrinter(PrintStream printStream)
+    {
+        ps = printStream;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        println("_____________________________________________________________________");
+        println(visitorInfo(programClassFile) + " CLASS: " + programClassFile.getName());
+        println("Access:        " + ClassUtil.externalClassAccessFlags(programClassFile.u2accessFlags) + "(" + Integer.toHexString(programClassFile.u2accessFlags) + ")");
+        println("Minor version: " + Integer.toHexString(programClassFile.u2minorVersion));
+        println("Major version: " + Integer.toHexString(programClassFile.u2majorVersion));
+        println("Access:        " + Integer.toHexString(programClassFile.u2accessFlags));
+        println("Superclass:    " + programClassFile.getSuperName());
+        println();
+
+        println("Interfaces (count = " + programClassFile.u2interfacesCount + "):");
+        indent();
+        for (int i = 0; i < programClassFile.u2interfacesCount; i++)
+        {
+            println("+ " + programClassFile.getCpClassNameString(programClassFile.u2interfaces[i]));
+        }
+        outdent();
+        println();
+
+        println("Constant Pool (count = " + programClassFile.u2constantPoolCount + "):");
+        indent();
+        programClassFile.constantPoolEntriesAccept(this);
+        outdent();
+        println();
+
+        println("Fields (count = " + programClassFile.u2fieldsCount + "):");
+        indent();
+        programClassFile.fieldsAccept(this);
+        outdent();
+        println();
+
+        println("Methods (count = " + programClassFile.u2methodsCount + "):");
+        indent();
+        programClassFile.methodsAccept(this);
+        outdent();
+        println();
+
+        println("Class file attributes (count = " + programClassFile.u2attributesCount + "):");
+        indent();
+        programClassFile.attributesAccept(this);
+        outdent();
+        println();
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        println("_____________________________________________________________________");
+        println(visitorInfo(libraryClassFile) + " LIBRARY CLASS: " + libraryClassFile.getName());
+        println("Access:     "  + ClassUtil.externalClassAccessFlags(libraryClassFile.u2accessFlags) + "(" + Integer.toHexString(libraryClassFile.u2accessFlags) + ")");
+        println("Superclass: "  + libraryClassFile.getSuperName());
+        println();
+
+        println("Interfaces (count = " + libraryClassFile.interfaceClasses.length + "):");
+        for (int i = 0; i < libraryClassFile.interfaceClasses.length; i++)
+        {
+            println("  + " + libraryClassFile.interfaceClasses[i].getName());
+        }
+
+        println("Fields (count = " + libraryClassFile.fields.length + "):");
+        libraryClassFile.fieldsAccept(this);
+
+        println("Methods (count = " + libraryClassFile.methods.length + "):");
+        libraryClassFile.methodsAccept(this);
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    {
+        println(visitorInfo(classCpInfo) + " Class [" +
+                classFile.getCpString(classCpInfo.u2nameIndex) + "]");
+    }
+
+
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo)
+    {
+        println(visitorInfo(doubleCpInfo) + " Double [" +
+                Double.longBitsToDouble(((long)doubleCpInfo.u4highBytes << 32) |
+                                         (long)doubleCpInfo.u4lowBytes) + "]");
+    }
+
+
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    {
+        println(visitorInfo(fieldrefCpInfo) + " Fieldref [" +
+                classFile.getCpClassNameString(fieldrefCpInfo.u2classIndex)  + "." +
+                classFile.getCpNameString(fieldrefCpInfo.u2nameAndTypeIndex) + " " +
+                classFile.getCpTypeString(fieldrefCpInfo.u2nameAndTypeIndex) + "]");
+    }
+
+
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo)
+    {
+        println(visitorInfo(floatCpInfo) + " Float [" +
+                Float.intBitsToFloat(floatCpInfo.u4bytes) + "]");
+    }
+
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo)
+    {
+        println(visitorInfo(integerCpInfo) + " Integer [" +
+                integerCpInfo.u4bytes + "]");
+    }
+
+
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    {
+        println(visitorInfo(interfaceMethodrefCpInfo) + " InterfaceMethodref [" +
+                classFile.getCpClassNameString(interfaceMethodrefCpInfo.u2classIndex)  + "." +
+                classFile.getCpNameString(interfaceMethodrefCpInfo.u2nameAndTypeIndex) + " " +
+                classFile.getCpTypeString(interfaceMethodrefCpInfo.u2nameAndTypeIndex) + "]");
+    }
+
+
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo)
+    {
+        println(visitorInfo(longCpInfo) + " Long [" +
+                (((long)longCpInfo.u4highBytes << 32) |
+                  (long)longCpInfo.u4lowBytes) + "]");
+    }
+
+
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    {
+        println(visitorInfo(methodrefCpInfo) + " Methodref [" +
+                classFile.getCpClassNameString(methodrefCpInfo.u2classIndex)  + "." +
+                classFile.getCpNameString(methodrefCpInfo.u2nameAndTypeIndex) + " " +
+                classFile.getCpTypeString(methodrefCpInfo.u2nameAndTypeIndex) + "]");
+    }
+
+
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo)
+    {
+        println(visitorInfo(nameAndTypeCpInfo) + " NameAndType [" +
+                classFile.getCpString(nameAndTypeCpInfo.u2nameIndex) + " " +
+                classFile.getCpString(nameAndTypeCpInfo.u2descriptorIndex) + "]");
+    }
+
+
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
+    {
+        println(visitorInfo(stringCpInfo) + " String [" +
+                classFile.getCpString(stringCpInfo.u2stringIndex) + "]");
+    }
+
+
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo)
+    {
+        println(visitorInfo(utf8CpInfo) + " Utf8 [" +
+                utf8CpInfo.getString() + "]");
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        visitMemberInfo(programClassFile, programFieldInfo);
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        visitMemberInfo(programClassFile, programMethodInfo);
+    }
+
+
+    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    {
+        println(visitorInfo(programMemberInfo) + " " +
+                programClassFile.getCpString(programMemberInfo.u2nameIndex) + " " +
+                programClassFile.getCpString(programMemberInfo.u2descriptorIndex));
+
+        if (programMemberInfo.u2attributesCount > 0)
+        {
+            indent();
+            println("Class member attributes (count = " + programMemberInfo.u2attributesCount + "):");
+            programMemberInfo.attributesAccept(programClassFile, this);
+            outdent();
+        }
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    {
+        visitLibraryMemberInfo(libraryClassFile, libraryFieldInfo);
+    }
+
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        visitLibraryMemberInfo(libraryClassFile, libraryMethodInfo);
+    }
+
+
+    private void visitLibraryMemberInfo(LibraryClassFile libraryClassFile, LibraryMemberInfo libraryMemberInfo)
+    {
+        println(visitorInfo(libraryMemberInfo) + " " +
+                libraryMemberInfo.getName(libraryClassFile) + " " +
+                libraryMemberInfo.getDescriptor(libraryClassFile));
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+    // Note that attributes are typically only referenced once, so we don't
+    // test if they are marked already.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo)
+    {
+        println(visitorInfo(unknownAttrInfo) +
+                " Unknown attribute (" + classFile.getCpString(unknownAttrInfo.u2attrNameIndex) + ")");
+    }
+
+
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
+    {
+        println(visitorInfo(innerClassesAttrInfo) +
+                " Inner classes attribute (count = " + innerClassesAttrInfo.u2numberOfClasses + ")");
+
+        indent();
+        innerClassesAttrInfo.innerClassEntriesAccept(classFile, this);
+        outdent();
+    }
+
+
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
+    {
+        println(visitorInfo(enclosingMethodAttrInfo) +
+                " Enclosing method attribute [" +
+                classFile.getCpClassNameString(enclosingMethodAttrInfo.u2classIndex)  +
+                (enclosingMethodAttrInfo.u2nameAndTypeIndex == 0 ? "" : "." +
+                 classFile.getCpNameString(enclosingMethodAttrInfo.u2nameAndTypeIndex) + " " +
+                 classFile.getCpTypeString(enclosingMethodAttrInfo.u2nameAndTypeIndex)) + "]");
+    }
+
+
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo)
+    {
+        println(visitorInfo(constantValueAttrInfo) +
+                " Constant value attribute:");
+
+        classFile.constantPoolEntryAccept(constantValueAttrInfo.u2constantValueIndex, this);
+    }
+
+
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo)
+    {
+        println(visitorInfo(exceptionsAttrInfo) +
+                " Exceptions attribute (count = " + exceptionsAttrInfo.u2numberOfExceptions + ")");
+
+        indent();
+        exceptionsAttrInfo.exceptionEntriesAccept((ProgramClassFile)classFile, this);
+        outdent();
+    }
+
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        println(visitorInfo(codeAttrInfo) +
+                " Code attribute instructions (code length = "+ codeAttrInfo.u4codeLength +
+                ", locals = "+ codeAttrInfo.u2maxLocals +
+                ", stack = "+ codeAttrInfo.u2maxStack + "):");
+
+        indent();
+
+        codeAttrInfo.instructionsAccept(classFile, methodInfo, this);
+
+        println("Code attribute exceptions (count = " +
+                codeAttrInfo.u2exceptionTableLength + "):");
+
+        codeAttrInfo.exceptionsAccept(classFile, methodInfo, this);
+
+        println("Code attribute attributes (attribute count = " +
+                codeAttrInfo.u2attributesCount + "):");
+
+        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
+
+        outdent();
+    }
+
+
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo)
+    {
+        println(visitorInfo(lineNumberTableAttrInfo) +
+                " Line number table attribute (count = " +
+                lineNumberTableAttrInfo.u2lineNumberTableLength + ")");
+        // ...
+    }
+
+
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
+    {
+        println(visitorInfo(localVariableTableAttrInfo) +
+                " Local variable table attribute (count = " +
+                localVariableTableAttrInfo.u2localVariableTableLength + ")");
+        // ...
+    }
+
+
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
+    {
+        println(visitorInfo(localVariableTypeTableAttrInfo) +
+                " Local variable type table attribute (count = "+
+                localVariableTypeTableAttrInfo.u2localVariableTypeTableLength + ")");
+        // ...
+    }
+
+
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo)
+    {
+        println(visitorInfo(sourceFileAttrInfo) +
+                " Source file attribute:");
+
+        indent();
+        classFile.constantPoolEntryAccept(sourceFileAttrInfo.u2sourceFileIndex, this);
+        outdent();
+    }
+
+
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo)
+    {
+        println(visitorInfo(sourceDirAttrInfo) +
+                " Source dir attribute:");
+
+        indent();
+        classFile.constantPoolEntryAccept(sourceDirAttrInfo.u2sourceDirIndex, this);
+        outdent();
+    }
+
+
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo)
+    {
+        println(visitorInfo(deprecatedAttrInfo) +
+                " Deprecated attribute");
+    }
+
+
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo)
+    {
+        println(visitorInfo(syntheticAttrInfo) +
+                " Synthetic attribute");
+    }
+
+
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
+    {
+        println(visitorInfo(signatureAttrInfo) +
+                " Signature attribute:");
+
+        indent();
+        classFile.constantPoolEntryAccept(signatureAttrInfo.u2signatureIndex, this);
+        outdent();
+    }
+
+
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
+    {
+        println(visitorInfo(runtimeVisibleAnnotationsAttrInfo) +
+                " Runtime visible annotation attribute:");
+
+        indent();
+        runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        outdent();
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
+    {
+        println(visitorInfo(runtimeInvisibleAnnotationsAttrInfo) +
+                " Runtime invisible annotation attribute:");
+
+        indent();
+        runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        outdent();
+    }
+
+
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
+    {
+        println(visitorInfo(runtimeVisibleParameterAnnotationsAttrInfo) +
+                " Runtime visible parameter annotation attribute:");
+
+        indent();
+        runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        outdent();
+    }
+
+
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
+    {
+        println(visitorInfo(runtimeInvisibleParameterAnnotationsAttrInfo) +
+                " Runtime invisible parameter annotation attribute:");
+
+        indent();
+        runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        outdent();
+    }
+
+
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
+    {
+        println(visitorInfo(annotationDefaultAttrInfo) +
+                " Annotation default attribute:");
+
+        indent();
+        annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
+        outdent();
+    }
+
+
+    // Implementations for InnerClassesInfoVisitor.
+
+    public void visitInnerClassesInfo(ClassFile classFile, InnerClassesInfo innerClassesInfo)
+    {
+        println(visitorInfo(innerClassesInfo) +
+                " InnerClassesInfo:");
+
+        indent();
+        if (innerClassesInfo.u2innerClassInfoIndex != 0)
+        {
+            classFile.constantPoolEntryAccept(innerClassesInfo.u2innerClassInfoIndex, this);
+        }
+
+        if (innerClassesInfo.u2outerClassInfoIndex != 0)
+        {
+            classFile.constantPoolEntryAccept(innerClassesInfo.u2outerClassInfoIndex, this);
+        }
+
+        if (innerClassesInfo.u2innerNameIndex != 0)
+        {
+            classFile.constantPoolEntryAccept(innerClassesInfo.u2innerNameIndex, this);
+        }
+        outdent();
+    }
+
+
+    // Implementations for AnnotationVisitor.
+
+    public void visitAnnotation(ClassFile classFile, Annotation annotation)
+    {
+        println(visitorInfo(annotation) +
+                " Annotation [" + classFile.getCpString(annotation.u2typeIndex) + "]:");
+
+        indent();
+        annotation.elementValuesAccept(classFile, this);
+        outdent();
+    }
+
+
+    // Implementations for ElementValueVisitor.
+
+    public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue)
+    {
+        println(visitorInfo(constantElementValue) +
+                " Constant element value [" +
+                (constantElementValue.u2elementName == 0 ? "(default)" :
+                classFile.getCpString(constantElementValue.u2elementName)) + "]");
+
+        indent();
+        classFile.constantPoolEntryAccept(constantElementValue.u2constantValueIndex, this);
+        outdent();
+    }
+
+
+    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+    {
+        println(visitorInfo(enumConstantElementValue) +
+                " Enum constant element value [" +
+                (enumConstantElementValue.u2elementName == 0 ? "(default)" :
+                classFile.getCpString(enumConstantElementValue.u2elementName)) + ", " +
+                classFile.getCpString(enumConstantElementValue.u2typeNameIndex)  + ", " +
+                classFile.getCpString(enumConstantElementValue.u2constantNameIndex) + "]");
+    }
+
+
+    public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
+    {
+        println(visitorInfo(classElementValue) +
+                " Class element value [" +
+                (classElementValue.u2elementName == 0 ? "(default)" :
+                classFile.getCpString(classElementValue.u2elementName)) + ", " +
+                classFile.getCpString(classElementValue.u2classInfoIndex) + "]");
+    }
+
+
+    public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue)
+    {
+        println(visitorInfo(annotationElementValue) +
+                " Annotation element value [" +
+                (annotationElementValue.u2elementName == 0 ? "(default)" :
+                classFile.getCpString(annotationElementValue.u2elementName)) + "]:");
+
+        indent();
+        annotationElementValue.annotationAccept(classFile, this);
+        outdent();
+    }
+
+
+    public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
+    {
+        println(visitorInfo(arrayElementValue) +
+                " Array element value [" +
+                (arrayElementValue.u2elementName == 0 ? "(default)" :
+                classFile.getCpString(arrayElementValue.u2elementName)) + "]:");
+
+        indent();
+        arrayElementValue.elementValuesAccept(classFile, annotation, this);
+        outdent();
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
+    {
+        println(InstructionFactory.create(codeAttrInfo.code, offset).toString(offset));
+    }
+
+
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    {
+        println(InstructionFactory.create(codeAttrInfo.code, offset).toString(offset));
+    }
+
+
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    {
+        println(InstructionFactory.create(codeAttrInfo.code, offset).toString(offset));
+
+        indent();
+        classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
+        outdent();
+    }
+
+
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+    {
+        println(InstructionFactory.create(codeAttrInfo.code, offset).toString(offset));
+    }
+
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
+    {
+        println(InstructionFactory.create(codeAttrInfo.code, offset).toString(offset));
+    }
+
+
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
+    {
+        println(InstructionFactory.create(codeAttrInfo.code, offset).toString(offset));
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo)
+    {
+        println(visitorInfo(exceptionInfo) +
+                " ExceptionInfo:");
+
+        if (exceptionInfo.u2catchType != 0)
+        {
+            classFile.constantPoolEntryAccept(exceptionInfo.u2catchType, this);
+        }
+    }
+
+
+    // Small utility methods.
+
+    private void indent()
+    {
+        indentation++;
+    }
+
+    private void outdent()
+    {
+        indentation--;
+    }
+
+    private void println()
+    {
+        ps.println();
+    }
+
+    private void println(String string)
+    {
+        for (int i = 0; i < indentation; i++)
+        {
+            ps.print(INDENTATION);
+        }
+
+        ps.println(string);
+    }
+
+
+    private String visitorInfo(VisitorAccepter visitorAccepter)
+    {
+        return visitorAccepter.getVisitorInfo() == null ? "-" : "+";
+    }
+}
diff --git a/src/proguard/classfile/visitor/ClassFileVisitor.java b/src/proguard/classfile/visitor/ClassFileVisitor.java
new file mode 100644
index 0000000..7723f7a
--- /dev/null
+++ b/src/proguard/classfile/visitor/ClassFileVisitor.java
@@ -0,0 +1,36 @@
+/* $Id: ClassFileVisitor.java,v 1.7 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This interface specifies the methods for a visitor of
+ * <code>ClassFile</code> objects.
+ *
+ * @author Eric Lafortune
+ */
+public interface ClassFileVisitor
+{
+    public void visitProgramClassFile(ProgramClassFile programClassFile);
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile);
+}
diff --git a/src/proguard/classfile/visitor/ClassPoolFiller.java b/src/proguard/classfile/visitor/ClassPoolFiller.java
new file mode 100644
index 0000000..19ac0c9
--- /dev/null
+++ b/src/proguard/classfile/visitor/ClassPoolFiller.java
@@ -0,0 +1,69 @@
+/* $Id: ClassPoolFiller.java,v 1.8 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.util.ClassUtil;
+
+
+/**
+ * This ClassFileVisitor collects all the class files it visits in a given
+ * class pool.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassPoolFiller implements ClassFileVisitor
+{
+    private ClassPool classPool;
+    private boolean   note;
+
+
+    public ClassPoolFiller(ClassPool classPool,
+                           boolean   note)
+    {
+        this.classPool = classPool;
+        this.note      = note;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        ClassFile previousClassFile = classPool.addClass(programClassFile);
+        if (previousClassFile != null &&
+            note)
+        {
+            System.err.println("Note: duplicate definition of program class [" + ClassUtil.externalClassName(programClassFile.getName()) + "]");
+        }
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        ClassFile previousClassFile = classPool.addClass(libraryClassFile);
+        if (previousClassFile != null &&
+            note)
+        {
+            System.err.println("Note: duplicate definition of library class [" + ClassUtil.externalClassName(libraryClassFile.getName()) + "]");
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/ClassPoolVisitor.java b/src/proguard/classfile/visitor/ClassPoolVisitor.java
new file mode 100644
index 0000000..111c6d9
--- /dev/null
+++ b/src/proguard/classfile/visitor/ClassPoolVisitor.java
@@ -0,0 +1,37 @@
+/* $Id: ClassPoolVisitor.java,v 1.9 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This interface specifies the methods for a visitor of
+ * <code>ClassPool</code> objects. Note that there is only a single
+ * implementation of <code>ClassPool</code>, such that this interface
+ * is not strictly necessary as a visitor.
+ *
+ * @author Eric Lafortune
+ */
+public interface ClassPoolVisitor
+{
+    public void visitClassPool(ClassPool classPool);
+}
diff --git a/src/proguard/classfile/visitor/ConcreteClassFileDownTraveler.java b/src/proguard/classfile/visitor/ConcreteClassFileDownTraveler.java
new file mode 100644
index 0000000..bba9771
--- /dev/null
+++ b/src/proguard/classfile/visitor/ConcreteClassFileDownTraveler.java
@@ -0,0 +1,100 @@
+/* $Id: ConcreteClassFileDownTraveler.java,v 1.7 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This <code>ClassFileVisitor</code> lets a given <code>ClassFileVisitor</code>
+ * travel to the first concrete subclasses down in its hierarchy of abstract
+ * classes and concrete classes.
+ *
+ * @author Eric Lafortune
+ */
+public class ConcreteClassFileDownTraveler
+  implements ClassFileVisitor
+{
+    private ClassFileVisitor classFileVisitor;
+
+
+    /**
+     * Creates a new ConcreteClassFileDownTraveler.
+     * @param classFileVisitor the <code>ClassFileVisitor</code> to
+     *                         which visits will be delegated.
+     */
+    public ConcreteClassFileDownTraveler(ClassFileVisitor classFileVisitor)
+    {
+        this.classFileVisitor = classFileVisitor;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Is this an abstract class or an interface?
+        if ((programClassFile.getAccessFlags() &
+             (ClassConstants.INTERNAL_ACC_INTERFACE |
+              ClassConstants.INTERNAL_ACC_ABSTRACT)) != 0)
+        {
+            // Travel down the hierarchy.
+            ClassFile[] subClasses = programClassFile.subClasses;
+            if (subClasses != null)
+            {
+                for (int i = 0; i < subClasses.length; i++)
+                {
+                    subClasses[i].accept(this);
+                }
+            }
+        }
+        else
+        {
+            // Visit the class file. Don't descend any further.
+            programClassFile.accept(classFileVisitor);
+        }
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        // Is this an abstract class or interface?
+        if ((libraryClassFile.getAccessFlags() &
+             (ClassConstants.INTERNAL_ACC_INTERFACE |
+              ClassConstants.INTERNAL_ACC_ABSTRACT)) != 0)
+        {
+            // Travel down the hierarchy.
+            ClassFile[] subClasses = libraryClassFile.subClasses;
+            if (subClasses != null)
+            {
+                for (int i = 0; i < subClasses.length; i++)
+                {
+                    subClasses[i].accept(this);
+                }
+            }
+        }
+        else
+        {
+            // Visit the class file. Don't descend any further.
+            libraryClassFile.accept(classFileVisitor);
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/CpInfoVisitor.java b/src/proguard/classfile/visitor/CpInfoVisitor.java
new file mode 100644
index 0000000..c4b190b
--- /dev/null
+++ b/src/proguard/classfile/visitor/CpInfoVisitor.java
@@ -0,0 +1,45 @@
+/* $Id: CpInfoVisitor.java,v 1.7 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This interface specifies the methods for a visitor of <code>CpInfo</code>
+ * objects.
+ *
+ * @author Eric Lafortune
+ */
+public interface CpInfoVisitor
+{
+    public void visitIntegerCpInfo(           ClassFile classFile, IntegerCpInfo            integerCpInfo);
+    public void visitLongCpInfo(              ClassFile classFile, LongCpInfo               longCpInfo);
+    public void visitFloatCpInfo(             ClassFile classFile, FloatCpInfo              floatCpInfo);
+    public void visitDoubleCpInfo(            ClassFile classFile, DoubleCpInfo             doubleCpInfo);
+    public void visitStringCpInfo(            ClassFile classFile, StringCpInfo             stringCpInfo);
+    public void visitUtf8CpInfo(              ClassFile classFile, Utf8CpInfo               utf8CpInfo);
+    public void visitFieldrefCpInfo(          ClassFile classFile, FieldrefCpInfo           fieldrefCpInfo);
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo);
+    public void visitMethodrefCpInfo(         ClassFile classFile, MethodrefCpInfo          methodrefCpInfo);
+    public void visitClassCpInfo(             ClassFile classFile, ClassCpInfo              classCpInfo);
+    public void visitNameAndTypeCpInfo(       ClassFile classFile, NameAndTypeCpInfo        nameAndTypeCpInfo);
+}
diff --git a/src/proguard/classfile/visitor/LibraryClassFileFilter.java b/src/proguard/classfile/visitor/LibraryClassFileFilter.java
new file mode 100644
index 0000000..203dd17
--- /dev/null
+++ b/src/proguard/classfile/visitor/LibraryClassFileFilter.java
@@ -0,0 +1,60 @@
+/* $Id: LibraryClassFileFilter.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This <code>ClassFileVisitor</code> delegates its visits to another given
+ * <code>ClassFileVisitor</code>, but only when visiting library class files.
+ *
+ * @author Eric Lafortune
+ */
+public class LibraryClassFileFilter implements ClassFileVisitor
+{
+    private ClassFileVisitor classFileVisitor;
+
+
+    /**
+     * Creates a new LibraryClassFileFilter.
+     * @param classFileVisitor the <code>ClassFileVisitor</code> to which visits
+     *                         will be delegated.
+     */
+    public LibraryClassFileFilter(ClassFileVisitor classFileVisitor)
+    {
+        this.classFileVisitor = classFileVisitor;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Don't delegate visits to program class files.
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        classFileVisitor.visitLibraryClassFile(libraryClassFile);
+    }
+}
diff --git a/src/proguard/classfile/visitor/LibraryMemberInfoFilter.java b/src/proguard/classfile/visitor/LibraryMemberInfoFilter.java
new file mode 100644
index 0000000..9d32e2e
--- /dev/null
+++ b/src/proguard/classfile/visitor/LibraryMemberInfoFilter.java
@@ -0,0 +1,73 @@
+/* $Id: LibraryMemberInfoFilter.java,v 1.4 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This <code>MemberInfoVisitor</code> delegates its visits to another given
+ * <code>MemberInfoVisitor</code>, but only when visiting members of library
+ * class files.
+ *
+ * @author Eric Lafortune
+ */
+public class LibraryMemberInfoFilter implements MemberInfoVisitor
+{
+    private MemberInfoVisitor memberInfoVisitor;
+
+
+    /**
+     * Creates a new ProgramMemberInfoFilter.
+     * @param memberInfoVisitor the <code>MemberInfoVisitor</code> to which
+     *                          visits will be delegated.
+     */
+    public LibraryMemberInfoFilter(MemberInfoVisitor memberInfoVisitor)
+    {
+        this.memberInfoVisitor = memberInfoVisitor;
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        // Don't delegate visits to program members.
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        // Don't delegate visits to program members.
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    {
+        memberInfoVisitor.visitLibraryFieldInfo(libraryClassFile, libraryFieldInfo);
+    }
+
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        memberInfoVisitor.visitLibraryMethodInfo(libraryClassFile, libraryMethodInfo);
+    }
+}
diff --git a/src/proguard/classfile/visitor/LineNumberInfoVisitor.java b/src/proguard/classfile/visitor/LineNumberInfoVisitor.java
new file mode 100644
index 0000000..61a2083
--- /dev/null
+++ b/src/proguard/classfile/visitor/LineNumberInfoVisitor.java
@@ -0,0 +1,38 @@
+/* $Id: LineNumberInfoVisitor.java,v 1.9 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+
+
+/**
+ * This interface specifies the methods for a visitor of
+ * <code>LineNumberInfo</code> objects. Note that there is only a single
+ * implementation of <code>LineNumberInfo</code>, such that this interface
+ * is not strictly necessary as a visitor.
+ *
+ * @author Eric Lafortune
+ */
+public interface LineNumberInfoVisitor
+{
+    public void visitLineNumberInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberInfo lineNumberInfo);
+}
diff --git a/src/proguard/classfile/visitor/MemberInfoAccessFilter.java b/src/proguard/classfile/visitor/MemberInfoAccessFilter.java
new file mode 100644
index 0000000..0c8ac62
--- /dev/null
+++ b/src/proguard/classfile/visitor/MemberInfoAccessFilter.java
@@ -0,0 +1,122 @@
+/* $Id: MemberInfoAccessFilter.java,v 1.6 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This <code>MemberInfoVisitor</code> delegates its visits to another given
+ * <code>MemberInfoVisitor</code>, but only when the visited member
+ * has the proper access flags.
+ * <p>
+ * If conflicting access flags (public/private/protected) are specified,
+ * having one of them set will be considered sufficient.
+ *
+ * @see ClassConstants
+ *
+ * @author Eric Lafortune
+ */
+public class MemberInfoAccessFilter
+  implements MemberInfoVisitor
+{
+    // A mask of conflicting access flags. These are interpreted in a special
+    // way if more of them are required at the same time. In that case, one
+    // of them being set is sufficient.
+    private static final int ACCESS_MASK =
+        ClassConstants.INTERNAL_ACC_PUBLIC  |
+        ClassConstants.INTERNAL_ACC_PRIVATE |
+        ClassConstants.INTERNAL_ACC_PROTECTED;
+
+    private int               requiredSetAccessFlags;
+    private int               requiredUnsetAccessFlags;
+    private int               requiredOneSetAccessFlags;
+    private MemberInfoVisitor memberInfoVisitor;
+
+
+    /**
+     * Creates a new MemberInfoAccessFilter.
+     * @param requiredSetAccessFlags   the class access flags that should be
+     *                                 set.
+     * @param requiredUnsetAccessFlags the class access flags that should be
+     *                                 unset.
+     * @param memberInfoVisitor        the <code>MemberInfoVisitor</code> to
+     *                                 which visits will be delegated.
+     */
+    public MemberInfoAccessFilter(int               requiredSetAccessFlags,
+                                  int               requiredUnsetAccessFlags,
+                                  MemberInfoVisitor memberInfoVisitor)
+    {
+        this.requiredSetAccessFlags    = requiredSetAccessFlags & ~ACCESS_MASK;
+        this.requiredUnsetAccessFlags  = requiredUnsetAccessFlags;
+        this.requiredOneSetAccessFlags = requiredSetAccessFlags &  ACCESS_MASK;
+        this.memberInfoVisitor         = memberInfoVisitor;
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        if (accepted(programFieldInfo.getAccessFlags()))
+        {
+            memberInfoVisitor.visitProgramFieldInfo(programClassFile, programFieldInfo);
+        }
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        if (accepted(programMethodInfo.getAccessFlags()))
+        {
+            memberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
+        }
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    {
+        if (accepted(libraryFieldInfo.getAccessFlags()))
+        {
+            memberInfoVisitor.visitLibraryFieldInfo(libraryClassFile, libraryFieldInfo);
+        }
+    }
+
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        if (accepted(libraryMethodInfo.getAccessFlags()))
+        {
+            memberInfoVisitor.visitLibraryMethodInfo(libraryClassFile, libraryMethodInfo);
+        }
+    }
+
+
+    // Small utility methods.
+
+    private boolean accepted(int accessFlags)
+    {
+        return (requiredSetAccessFlags    & ~accessFlags) == 0 &&
+               (requiredUnsetAccessFlags  &  accessFlags) == 0 &&
+               (requiredOneSetAccessFlags == 0                 ||
+               (requiredOneSetAccessFlags &  accessFlags) != 0);
+    }
+}
diff --git a/src/proguard/classfile/visitor/MemberInfoDescriptorFilter.java b/src/proguard/classfile/visitor/MemberInfoDescriptorFilter.java
new file mode 100644
index 0000000..3475e46
--- /dev/null
+++ b/src/proguard/classfile/visitor/MemberInfoDescriptorFilter.java
@@ -0,0 +1,99 @@
+/* $Id: MemberInfoDescriptorFilter.java,v 1.9 2004/11/27 10:09:26 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.util.*;
+
+
+/**
+ * This <code>MemberInfoVisitor</code> delegates its visits to another given
+ * <code>MemberInfoVisitor</code>, but only when the visited member
+ * has a descriptor that matches a given regular expression.
+ *
+ * @author Eric Lafortune
+ */
+public class MemberInfoDescriptorFilter implements MemberInfoVisitor
+{
+    private StringMatcher     regularExpressionMatcher;
+    private MemberInfoVisitor memberInfoVisitor;
+
+
+    /**
+     * Creates a new MemberInfoDescriptorFilter.
+     * @param regularExpression the regular expression against which class names
+     *                          will be matched.
+     * @param memberInfoVisitor the <code>MemberInfoVisitor</code> to which visits
+     *                          will be delegated.
+     */
+    public MemberInfoDescriptorFilter(String            regularExpression,
+                                      MemberInfoVisitor memberInfoVisitor)
+    {
+        this.regularExpressionMatcher = new ClassNameMatcher(regularExpression);
+        this.memberInfoVisitor        = memberInfoVisitor;
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        if (accepted(programFieldInfo.getDescriptor(programClassFile)))
+        {
+            memberInfoVisitor.visitProgramFieldInfo(programClassFile, programFieldInfo);
+        }
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        if (accepted(programMethodInfo.getDescriptor(programClassFile)))
+        {
+            memberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
+        }
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    {
+        if (accepted(libraryFieldInfo.getDescriptor(libraryClassFile)))
+        {
+            memberInfoVisitor.visitLibraryFieldInfo(libraryClassFile, libraryFieldInfo);
+        }
+    }
+
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        if (accepted(libraryMethodInfo.getDescriptor(libraryClassFile)))
+        {
+            memberInfoVisitor.visitLibraryMethodInfo(libraryClassFile, libraryMethodInfo);
+        }
+    }
+
+
+    // Small utility methods.
+
+    private boolean accepted(String name)
+    {
+        return regularExpressionMatcher.matches(name);
+    }
+}
diff --git a/src/proguard/classfile/visitor/MemberInfoNameFilter.java b/src/proguard/classfile/visitor/MemberInfoNameFilter.java
new file mode 100644
index 0000000..1da77ed
--- /dev/null
+++ b/src/proguard/classfile/visitor/MemberInfoNameFilter.java
@@ -0,0 +1,99 @@
+/* $Id: MemberInfoNameFilter.java,v 1.9 2004/11/27 10:09:26 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.util.*;
+
+
+/**
+ * This <code>MemberInfoVisitor</code> delegates its visits to another given
+ * <code>MemberInfoVisitor</code>, but only when the visited member
+ * has a name that matches a given regular expression.
+ *
+ * @author Eric Lafortune
+ */
+public class MemberInfoNameFilter implements MemberInfoVisitor
+{
+    private StringMatcher     regularExpressionMatcher;
+    private MemberInfoVisitor memberInfoVisitor;
+
+
+    /**
+     * Creates a new MemberInfoNameFilter.
+     * @param regularExpression the regular expression against which class names
+     *                          will be matched.
+     * @param memberInfoVisitor the <code>MemberInfoVisitor</code> to which visits
+     *                          will be delegated.
+     */
+    public MemberInfoNameFilter(String            regularExpression,
+                                MemberInfoVisitor memberInfoVisitor)
+    {
+        this.regularExpressionMatcher = new ClassNameMatcher(regularExpression);
+        this.memberInfoVisitor        = memberInfoVisitor;
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        if (accepted(programFieldInfo.getName(programClassFile)))
+        {
+            memberInfoVisitor.visitProgramFieldInfo(programClassFile, programFieldInfo);
+        }
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        if (accepted(programMethodInfo.getName(programClassFile)))
+        {
+            memberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
+        }
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    {
+        if (accepted(libraryFieldInfo.getName(libraryClassFile)))
+        {
+            memberInfoVisitor.visitLibraryFieldInfo(libraryClassFile, libraryFieldInfo);
+        }
+    }
+
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        if (accepted(libraryMethodInfo.getName(libraryClassFile)))
+        {
+            memberInfoVisitor.visitLibraryMethodInfo(libraryClassFile, libraryMethodInfo);
+        }
+    }
+
+
+    // Small utility methods.
+
+    private boolean accepted(String name)
+    {
+        return regularExpressionMatcher.matches(name);
+    }
+}
diff --git a/src/proguard/classfile/visitor/MemberInfoVisitor.java b/src/proguard/classfile/visitor/MemberInfoVisitor.java
new file mode 100644
index 0000000..1a36ebd
--- /dev/null
+++ b/src/proguard/classfile/visitor/MemberInfoVisitor.java
@@ -0,0 +1,40 @@
+/* $Id: MemberInfoVisitor.java,v 1.10 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This interface specifies the methods for a visitor of
+ * <code>ProgramMemberInfo</code> objects and <code>LibraryMemberInfo</code>
+ * objects.
+ *
+ * @author Eric Lafortune
+ */
+public interface MemberInfoVisitor
+{
+    public void visitProgramFieldInfo( ProgramClassFile programClassFile, ProgramFieldInfo  programFieldInfo);
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo);
+
+    public void visitLibraryFieldInfo( LibraryClassFile libraryClassFile, LibraryFieldInfo  libraryFieldInfo);
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo);
+}
diff --git a/src/proguard/classfile/visitor/MultiClassFileVisitor.java b/src/proguard/classfile/visitor/MultiClassFileVisitor.java
new file mode 100644
index 0000000..ac6abc1
--- /dev/null
+++ b/src/proguard/classfile/visitor/MultiClassFileVisitor.java
@@ -0,0 +1,97 @@
+/* $Id: MultiClassFileVisitor.java,v 1.8 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This ClassFileVisitor delegates all visits to each ClassFileVisitor
+ * in a given list.
+ *
+ * @author Eric Lafortune
+ */
+public class MultiClassFileVisitor implements ClassFileVisitor
+{
+    private static final int ARRAY_SIZE_INCREMENT = 5;
+
+    private ClassFileVisitor[] classFileVisitors;
+    private int                classFileVisitorCount;
+
+
+    public MultiClassFileVisitor()
+    {
+    }
+
+
+    public MultiClassFileVisitor(ClassFileVisitor[] classFileVisitors)
+    {
+        this.classFileVisitors     = classFileVisitors;
+        this.classFileVisitorCount = classFileVisitors.length;
+    }
+
+
+    public void addClassFileVisitor(ClassFileVisitor classFileVisitor)
+    {
+        ensureArraySize();
+
+        classFileVisitors[classFileVisitorCount++] = classFileVisitor;
+    }
+
+
+    private void ensureArraySize()
+    {
+        if (classFileVisitors == null)
+        {
+            classFileVisitors = new ClassFileVisitor[ARRAY_SIZE_INCREMENT];
+        }
+        else if (classFileVisitors.length == classFileVisitorCount)
+        {
+            ClassFileVisitor[] newClassFileVisitors =
+                new ClassFileVisitor[classFileVisitorCount +
+                                     ARRAY_SIZE_INCREMENT];
+            System.arraycopy(classFileVisitors, 0,
+                             newClassFileVisitors, 0,
+                             classFileVisitorCount);
+            classFileVisitors = newClassFileVisitors;
+        }
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        for (int i = 0; i < classFileVisitorCount; i++)
+        {
+            classFileVisitors[i].visitProgramClassFile(programClassFile);
+        }
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        for (int i = 0; i < classFileVisitorCount; i++)
+        {
+            classFileVisitors[i].visitLibraryClassFile(libraryClassFile);
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/MultiClassPoolVisitor.java b/src/proguard/classfile/visitor/MultiClassPoolVisitor.java
new file mode 100644
index 0000000..dbc3696
--- /dev/null
+++ b/src/proguard/classfile/visitor/MultiClassPoolVisitor.java
@@ -0,0 +1,88 @@
+/* $Id: MultiClassPoolVisitor.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This ClassPoolVisitor delegates all visits to each ClassPoolVisitor
+ * in a given list.
+ *
+ * @author Eric Lafortune
+ */
+public class MultiClassPoolVisitor implements ClassPoolVisitor
+{
+    private static final int ARRAY_SIZE_INCREMENT = 5;
+
+    private ClassPoolVisitor[] classPoolVisitors;
+    private int                classPoolVisitorCount;
+
+
+    public MultiClassPoolVisitor()
+    {
+    }
+
+
+    public MultiClassPoolVisitor(ClassPoolVisitor[] classPoolVisitors)
+    {
+        this.classPoolVisitors     = classPoolVisitors;
+        this.classPoolVisitorCount = classPoolVisitors.length;
+    }
+
+
+    public void addClassPoolVisitor(ClassPoolVisitor classPoolVisitor)
+    {
+        ensureArraySize();
+
+        classPoolVisitors[classPoolVisitorCount++] = classPoolVisitor;
+    }
+
+
+    private void ensureArraySize()
+    {
+        if (classPoolVisitors == null)
+        {
+            classPoolVisitors = new ClassPoolVisitor[ARRAY_SIZE_INCREMENT];
+        }
+        else if (classPoolVisitors.length == classPoolVisitorCount)
+        {
+            ClassPoolVisitor[] newClassPoolVisitors =
+                new ClassPoolVisitor[classPoolVisitorCount +
+                                     ARRAY_SIZE_INCREMENT];
+            System.arraycopy(classPoolVisitors, 0,
+                             newClassPoolVisitors, 0,
+                             classPoolVisitorCount);
+            classPoolVisitors = newClassPoolVisitors;
+        }
+    }
+
+
+    // Implementations for ClassPoolVisitor.
+
+    public void visitClassPool(ClassPool classPool)
+    {
+        for (int i = 0; i < classPoolVisitorCount; i++)
+        {
+            classPoolVisitors[i].visitClassPool(classPool);
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/MultiMemberInfoVisitor.java b/src/proguard/classfile/visitor/MultiMemberInfoVisitor.java
new file mode 100644
index 0000000..5f4dda1
--- /dev/null
+++ b/src/proguard/classfile/visitor/MultiMemberInfoVisitor.java
@@ -0,0 +1,113 @@
+/* $Id: MultiMemberInfoVisitor.java,v 1.11 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This MemberInfoVisitor delegates all visits to each MemberInfoVisitor
+ * in a given list.
+ *
+ * @author Eric Lafortune
+ */
+public class MultiMemberInfoVisitor implements MemberInfoVisitor
+{
+    private static final int ARRAY_SIZE_INCREMENT = 5;
+
+    private MemberInfoVisitor[] memberInfoVisitors;
+    private int                 memberInfoVisitorCount;
+
+
+    public MultiMemberInfoVisitor()
+    {
+    }
+
+
+    public MultiMemberInfoVisitor(MemberInfoVisitor[] memberInfoVisitors)
+    {
+        this.memberInfoVisitors     = memberInfoVisitors;
+        this.memberInfoVisitorCount = memberInfoVisitors.length;
+    }
+
+
+    public void addMemberInfoVisitor(MemberInfoVisitor memberInfoVisitor)
+    {
+        ensureArraySize();
+
+        memberInfoVisitors[memberInfoVisitorCount++] = memberInfoVisitor;
+    }
+
+
+    private void ensureArraySize()
+    {
+        if (memberInfoVisitors == null)
+        {
+            memberInfoVisitors = new MemberInfoVisitor[ARRAY_SIZE_INCREMENT];
+        }
+        else if (memberInfoVisitors.length == memberInfoVisitorCount)
+        {
+            MemberInfoVisitor[] newMemberInfoVisitors =
+                new MemberInfoVisitor[memberInfoVisitorCount +
+                                         ARRAY_SIZE_INCREMENT];
+            System.arraycopy(memberInfoVisitors, 0,
+                             newMemberInfoVisitors, 0,
+                             memberInfoVisitorCount);
+            memberInfoVisitors = newMemberInfoVisitors;
+        }
+    }
+
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        for (int i = 0; i < memberInfoVisitorCount; i++)
+        {
+            memberInfoVisitors[i].visitProgramFieldInfo(programClassFile, programFieldInfo);
+        }
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        for (int i = 0; i < memberInfoVisitorCount; i++)
+        {
+            memberInfoVisitors[i].visitProgramMethodInfo(programClassFile, programMethodInfo);
+        }
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    {
+        for (int i = 0; i < memberInfoVisitorCount; i++)
+        {
+            memberInfoVisitors[i].visitLibraryFieldInfo(libraryClassFile, libraryFieldInfo);
+        }
+    }
+
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        for (int i = 0; i < memberInfoVisitorCount; i++)
+        {
+            memberInfoVisitors[i].visitLibraryMethodInfo(libraryClassFile, libraryMethodInfo);
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/NamedClassFileVisitor.java b/src/proguard/classfile/visitor/NamedClassFileVisitor.java
new file mode 100644
index 0000000..550487c
--- /dev/null
+++ b/src/proguard/classfile/visitor/NamedClassFileVisitor.java
@@ -0,0 +1,55 @@
+/* $Id: NamedClassFileVisitor.java,v 1.8 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This class visits ClassFile objects with the given name.
+ *
+ * @author Eric Lafortune
+ */
+public class NamedClassFileVisitor implements ClassPoolVisitor
+{
+    private ClassFileVisitor classFileVisitor;
+    private String           name;
+
+
+    public NamedClassFileVisitor(ClassFileVisitor classFileVisitor,
+                                 String           name)
+    {
+        this.classFileVisitor = classFileVisitor;
+        this.name             = name;
+    }
+
+
+    public void visitClassPool(ClassPool classPool)
+    {
+        try
+        {
+            classPool.classFileAccept(classFileVisitor, name);
+        }
+        catch (IllegalArgumentException ex)
+        {
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/NamedFieldVisitor.java b/src/proguard/classfile/visitor/NamedFieldVisitor.java
new file mode 100644
index 0000000..c1884db
--- /dev/null
+++ b/src/proguard/classfile/visitor/NamedFieldVisitor.java
@@ -0,0 +1,59 @@
+/* $Id: NamedFieldVisitor.java,v 1.11 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This class visits ProgramMemberInfo objects referring to fields, identified by
+ * a name and descriptor pair.
+ *
+ * @author Eric Lafortune
+ */
+public class NamedFieldVisitor implements ClassFileVisitor
+{
+    private String            name;
+    private String            descriptor;
+    private MemberInfoVisitor memberInfoVisitor;
+
+
+    public NamedFieldVisitor(String            name,
+                             String            descriptor,
+                             MemberInfoVisitor memberInfoVisitor)
+    {
+        this.name              = name;
+        this.descriptor        = descriptor;
+        this.memberInfoVisitor = memberInfoVisitor;
+    }
+
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        programClassFile.fieldAccept(name, descriptor, memberInfoVisitor);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        libraryClassFile.fieldAccept(name, descriptor, memberInfoVisitor);
+    }
+}
diff --git a/src/proguard/classfile/visitor/NamedMethodVisitor.java b/src/proguard/classfile/visitor/NamedMethodVisitor.java
new file mode 100644
index 0000000..fdaed64
--- /dev/null
+++ b/src/proguard/classfile/visitor/NamedMethodVisitor.java
@@ -0,0 +1,59 @@
+/* $Id: NamedMethodVisitor.java,v 1.11 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This class visits ProgramMemberInfo objects referring to methods, identified by
+ * a name and descriptor pair.
+ *
+ * @author Eric Lafortune
+ */
+public class NamedMethodVisitor implements ClassFileVisitor
+{
+    private String            name;
+    private String            descriptor;
+    private MemberInfoVisitor memberInfoVisitor;
+
+
+    public NamedMethodVisitor(String            name,
+                              String            descriptor,
+                              MemberInfoVisitor memberInfoVisitor)
+    {
+        this.name              = name;
+        this.descriptor        = descriptor;
+        this.memberInfoVisitor = memberInfoVisitor;
+    }
+
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        programClassFile.methodAccept(name, descriptor, memberInfoVisitor);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        libraryClassFile.methodAccept(name, descriptor, memberInfoVisitor);
+    }
+}
diff --git a/src/proguard/classfile/visitor/ProgramClassFileFilter.java b/src/proguard/classfile/visitor/ProgramClassFileFilter.java
new file mode 100644
index 0000000..b825e97
--- /dev/null
+++ b/src/proguard/classfile/visitor/ProgramClassFileFilter.java
@@ -0,0 +1,60 @@
+/* $Id: ProgramClassFileFilter.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This <code>ClassFileVisitor</code> delegates its visits to another given
+ * <code>ClassFileVisitor</code>, but only when visiting program class files.
+ *
+ * @author Eric Lafortune
+ */
+public class ProgramClassFileFilter implements ClassFileVisitor
+{
+    private ClassFileVisitor classFileVisitor;
+
+
+    /**
+     * Creates a new ProgramClassFileFilter.
+     * @param classFileVisitor the <code>ClassFileVisitor</code> to which visits
+     *                         will be delegated.
+     */
+    public ProgramClassFileFilter(ClassFileVisitor classFileVisitor)
+    {
+        this.classFileVisitor = classFileVisitor;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        classFileVisitor.visitProgramClassFile(programClassFile);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        // Don't delegate visits to library class files.
+    }
+}
diff --git a/src/proguard/classfile/visitor/ProgramMemberInfoFilter.java b/src/proguard/classfile/visitor/ProgramMemberInfoFilter.java
new file mode 100644
index 0000000..82a9cb5
--- /dev/null
+++ b/src/proguard/classfile/visitor/ProgramMemberInfoFilter.java
@@ -0,0 +1,73 @@
+/* $Id: ProgramMemberInfoFilter.java,v 1.4 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This <code>MemberInfoVisitor</code> delegates its visits to another given
+ * <code>MemberInfoVisitor</code>, but only when visiting members of program
+ * class files.
+ *
+ * @author Eric Lafortune
+ */
+public class ProgramMemberInfoFilter implements MemberInfoVisitor
+{
+    private MemberInfoVisitor memberInfoVisitor;
+
+
+    /**
+     * Creates a new ProgramMemberInfoFilter.
+     * @param memberInfoVisitor the <code>MemberInfoVisitor</code> to which
+     *                          visits will be delegated.
+     */
+    public ProgramMemberInfoFilter(MemberInfoVisitor memberInfoVisitor)
+    {
+        this.memberInfoVisitor = memberInfoVisitor;
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        memberInfoVisitor.visitProgramFieldInfo(programClassFile, programFieldInfo);
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        memberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    {
+        // Don't delegate visits to library members.
+    }
+
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        // Don't delegate visits to library members.
+    }
+}
diff --git a/src/proguard/classfile/visitor/ReferencedClassFileVisitor.java b/src/proguard/classfile/visitor/ReferencedClassFileVisitor.java
new file mode 100644
index 0000000..12a89e5
--- /dev/null
+++ b/src/proguard/classfile/visitor/ReferencedClassFileVisitor.java
@@ -0,0 +1,110 @@
+/* $Id: ReferencedClassFileVisitor.java,v 1.4 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This CpInfoVisitor lets a given ClassFileVisitor visit all the referenced
+ * class files of the constant pool entries that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class ReferencedClassFileVisitor
+  implements CpInfoVisitor
+{
+    private ClassFileVisitor classFileVisitor;
+
+
+    public ReferencedClassFileVisitor(ClassFileVisitor classFileVisitor)
+    {
+        this.classFileVisitor = classFileVisitor;
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+
+
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
+    {
+        visitReferencedClassFile(stringCpInfo.referencedClassFile);
+    }
+
+
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    {
+        visitReferencedClassFile(fieldrefCpInfo.referencedClassFile);
+    }
+
+
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    {
+        visitReferencedClassFile(interfaceMethodrefCpInfo.referencedClassFile);
+    }
+
+
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    {
+        visitReferencedClassFile(methodrefCpInfo.referencedClassFile);
+    }
+
+
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    {
+        visitReferencedClassFile(classCpInfo.referencedClassFile);
+    }
+
+
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo)
+    {
+        visitReferencedClassFiles(nameAndTypeCpInfo.referencedClassFiles);
+    }
+
+
+    // Small utility methods.
+
+    private void visitReferencedClassFiles(ClassFile[] referencedClassFiles)
+    {
+        if (referencedClassFiles != null)
+        {
+            for (int index = 0; index < referencedClassFiles.length; index++)
+            {
+                visitReferencedClassFile(referencedClassFiles[index]);
+            }
+        }
+    }
+
+
+    private void visitReferencedClassFile(ClassFile referencedClassFile)
+    {
+        if (referencedClassFile != null)
+        {
+            referencedClassFile.accept(classFileVisitor);
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/SimpleClassFilePrinter.java b/src/proguard/classfile/visitor/SimpleClassFilePrinter.java
new file mode 100644
index 0000000..c09ac46
--- /dev/null
+++ b/src/proguard/classfile/visitor/SimpleClassFilePrinter.java
@@ -0,0 +1,169 @@
+/* $Id: SimpleClassFilePrinter.java,v 1.17 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.util.*;
+import proguard.optimize.*;
+import proguard.optimize.SideEffectMethodMarker;
+
+import java.io.*;
+
+
+/**
+ * This <code>ClassFileVisitor</code> and <code>MemberInfoVisitor</code>
+ * prints out the class names of the class files it visits, and the full class
+ * member descriptions of the class members it visits. The names are printed
+ * in a readable, Java-like format. The access modifiers can be included or not.
+ *
+ * @author Eric Lafortune
+ */
+public class SimpleClassFilePrinter
+  implements ClassFileVisitor,
+             MemberInfoVisitor
+{
+    private boolean     printAccessModifiers;
+    private PrintStream ps;
+
+
+    /**
+     * Creates a new SimpleClassFilePrinter that prints to
+     * <code>System.out</code>, including the access modifiers.
+     */
+    public SimpleClassFilePrinter()
+    {
+        this(true);
+    }
+
+    /**
+     * Creates a new SimpleClassFilePrinter that prints to
+     * <code>System.out</code>, with or without the access modifiers.
+     */
+    public SimpleClassFilePrinter(boolean printAccessModifiers)
+    {
+        this(printAccessModifiers, System.out);
+    }
+
+    /**
+     * Creates a new SimpleClassFilePrinter that prints to the given
+     * <code>PrintStream</code>, with or without the access modifiers.
+     */
+    public SimpleClassFilePrinter(boolean     printAccessModifiers,
+                                  PrintStream printStream)
+    {
+        this.printAccessModifiers = printAccessModifiers;
+        this.ps                   = printStream;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        ps.println(ClassUtil.externalFullClassDescription(
+                       printAccessModifiers ?
+                           programClassFile.getAccessFlags() :
+                           0,
+                       programClassFile.getName()));
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        ps.println(ClassUtil.externalFullClassDescription(
+                       printAccessModifiers ?
+                           libraryClassFile.getAccessFlags() :
+                           0,
+                       libraryClassFile.getName()));
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        ps.println(ClassUtil.externalFullClassDescription(
+                       printAccessModifiers ?
+                           programClassFile.getAccessFlags() :
+                           0,
+                       programClassFile.getName()) +
+                   ": " +
+                   ClassUtil.externalFullFieldDescription(
+                       printAccessModifiers ?
+                           programFieldInfo.getAccessFlags() :
+                           0,
+                       programFieldInfo.getName(programClassFile),
+                       programFieldInfo.getDescriptor(programClassFile)));
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        ps.println(ClassUtil.externalFullClassDescription(
+                       printAccessModifiers ?
+                           programClassFile.getAccessFlags() :
+                           0,
+                       programClassFile.getName()) +
+                   ": " +
+                   ClassUtil.externalFullMethodDescription(
+                       programClassFile.getName(),
+                       printAccessModifiers ?
+                           programMethodInfo.getAccessFlags() :
+                           0,
+                       programMethodInfo.getName(programClassFile),
+                       programMethodInfo.getDescriptor(programClassFile)));
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    {
+        ps.println(ClassUtil.externalFullClassDescription(
+                       printAccessModifiers ?
+                           libraryClassFile.getAccessFlags() :
+                           0,
+                       libraryClassFile.getName()) +
+                   ": " +
+                   ClassUtil.externalFullFieldDescription(
+                       printAccessModifiers ?
+                           libraryFieldInfo.getAccessFlags() :
+                           0,
+                       libraryFieldInfo.getName(libraryClassFile),
+                       libraryFieldInfo.getDescriptor(libraryClassFile)));
+    }
+
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        ps.println(ClassUtil.externalFullClassDescription(
+                       printAccessModifiers ?
+                           libraryClassFile.getAccessFlags() :
+                           0,
+                       libraryClassFile.getName()) +
+                   ": " +
+                   ClassUtil.externalFullMethodDescription(
+                       libraryClassFile.getName(),
+                       printAccessModifiers ?
+                           libraryMethodInfo.getAccessFlags() :
+                           0,
+                       libraryMethodInfo.getName(libraryClassFile),
+                       libraryMethodInfo.getDescriptor(libraryClassFile)));
+    }
+}
diff --git a/src/proguard/classfile/visitor/VariableClassFileVisitor.java b/src/proguard/classfile/visitor/VariableClassFileVisitor.java
new file mode 100644
index 0000000..ab555a8
--- /dev/null
+++ b/src/proguard/classfile/visitor/VariableClassFileVisitor.java
@@ -0,0 +1,78 @@
+/* $Id: VariableClassFileVisitor.java,v 1.7 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This ClassFileVisitor delegates all method calls to a ClassFileVisitor
+ * that can be changed at any time.
+ *
+ * @author Eric Lafortune
+ */
+public class VariableClassFileVisitor implements ClassFileVisitor
+{
+    private ClassFileVisitor classFileVisitor;
+
+
+    public VariableClassFileVisitor()
+    {
+        this(null);
+    }
+
+
+    public VariableClassFileVisitor(ClassFileVisitor classFileVisitor)
+    {
+        this.classFileVisitor = classFileVisitor;
+    }
+
+
+    public void setClassFileVisitor(ClassFileVisitor classFileVisitor)
+    {
+        this.classFileVisitor = classFileVisitor;
+    }
+
+    public ClassFileVisitor getClassFileVisitor()
+    {
+        return classFileVisitor;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        if (classFileVisitor != null)
+        {
+            classFileVisitor.visitProgramClassFile(programClassFile);
+        }
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        if (classFileVisitor != null)
+        {
+            classFileVisitor.visitLibraryClassFile(libraryClassFile);
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/VariableMemberInfoVisitor.java b/src/proguard/classfile/visitor/VariableMemberInfoVisitor.java
new file mode 100644
index 0000000..2fdeef3
--- /dev/null
+++ b/src/proguard/classfile/visitor/VariableMemberInfoVisitor.java
@@ -0,0 +1,96 @@
+/* $Id: VariableMemberInfoVisitor.java,v 1.11 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This MemberInfoVisitor delegates all method calls to a MemberInfoVisitor
+ * that can be changed at any time.
+ *
+ * @author Eric Lafortune
+ */
+public class VariableMemberInfoVisitor implements MemberInfoVisitor
+{
+    private MemberInfoVisitor memberInfoVisitor;
+
+
+    public VariableMemberInfoVisitor()
+    {
+        this(null);
+    }
+
+
+    public VariableMemberInfoVisitor(MemberInfoVisitor memberInfoVisitor)
+    {
+        this.memberInfoVisitor = memberInfoVisitor;
+    }
+
+
+    public void setMemberInfoVisitor(MemberInfoVisitor memberInfoVisitor)
+    {
+        this.memberInfoVisitor = memberInfoVisitor;
+    }
+
+    public MemberInfoVisitor getMemberInfoVisitor()
+    {
+        return memberInfoVisitor;
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        if (memberInfoVisitor != null)
+        {
+            memberInfoVisitor.visitProgramFieldInfo(programClassFile, programFieldInfo);
+        }
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        if (memberInfoVisitor != null)
+        {
+            memberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
+        }
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    {
+        if (memberInfoVisitor != null)
+        {
+            memberInfoVisitor.visitLibraryFieldInfo(libraryClassFile, libraryFieldInfo);
+        }
+    }
+
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        if (memberInfoVisitor != null)
+        {
+            memberInfoVisitor.visitLibraryMethodInfo(libraryClassFile, libraryMethodInfo);
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/package.html b/src/proguard/classfile/visitor/package.html
new file mode 100644
index 0000000..d3be40c
--- /dev/null
+++ b/src/proguard/classfile/visitor/package.html
@@ -0,0 +1,40 @@
+<body>
+This package contains interfaces and classes for processing class files from
+the <code>{@link proguard.classfile proguard.classfile}</code> package using
+the <i>visitor pattern</i>. Cfr., for instance, "Design Patterns, Elements of
+Reusable OO Software", by Gamma, Helm, Johnson, and Vlissider.
+<p>
+Why the visitor pattern? Class files frequently contain lists of elements of
+various mixed types: class items, constant pool entries, attributes,...
+These lists and types are largely fixed; they won't change much in future
+releases of the Java class file specifications. On the other hand, the kinds
+of operations that we may wish to perform on the class files may change and
+expand. We want to separate the objects and the operations performed upon them.
+This is a good place to use the visitor pattern.
+<p>
+Visitor interfaces avoid having to do series of <code>instanceof</code> tests
+on the elements of a list, followed by type casts and the proper operations.
+Every list element is a visitor accepter. When its <code>accept</code> method
+is called by a visitor, it calls its corresponding <code>visitX</code> method
+in the visitor, passing itself as an argument. This technique is called
+double-dispatch.
+<p>
+As already mentioned, the main advantage is avoiding lots of
+<code>instanceof</code> tests and type casts. Also, implementing a visitor
+interface ensures you're handling all possible visitor accepter types. Each
+type has its own method, which you simply have to implement.
+<p>
+A disadvantage is that the visitor methods always get the same names, specified
+by the visitor interface. These names aren't descriptive at all, making code
+harder to read. It's the visitor classes that describe the operations now.
+<p>
+Also, the visitor methods always have the same parameters and return values, as
+specified by the visitor interfaces. Passing additional parameters is done by
+means of extra fields in the visitor, which is somewhat of a kludge.
+<p>
+Because objects (the visitor accepters) and the operations performed upon them
+(the visitors) are now separated, it becomes harder to associate some state
+with the objects. For convenience, we always provide an extra <i>visitor
+info</i> field in visitor accepters, in which visitors can put any temporary
+information they want.
+</body>
diff --git a/src/proguard/gui/ClassMemberSpecificationDialog.java b/src/proguard/gui/ClassMemberSpecificationDialog.java
new file mode 100644
index 0000000..6777bff
--- /dev/null
+++ b/src/proguard/gui/ClassMemberSpecificationDialog.java
@@ -0,0 +1,416 @@
+/* $Id: ClassMemberSpecificationDialog.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.border.Border;
+
+import proguard.*;
+import proguard.classfile.*;
+import proguard.classfile.util.*;
+import proguard.util.*;
+
+/**
+ * This <code>JDialog</code> allows the user to enter a String.
+ *
+ * @author Eric Lafortune
+ */
+class ClassMemberSpecificationDialog extends JDialog
+{
+    /**
+     * Return value if the dialog is canceled (with the Cancel button or by
+     * closing the dialog window).
+     */
+    public static final int CANCEL_OPTION = 1;
+
+    /**
+     * Return value if the dialog is approved (with the Ok button).
+     */
+    public static final int APPROVE_OPTION = 0;
+
+
+    private boolean isField;
+
+    private JRadioButton[] publicRadioButtons;
+    private JRadioButton[] privateRadioButtons;
+    private JRadioButton[] protectedRadioButtons;
+    private JRadioButton[] staticRadioButtons;
+    private JRadioButton[] finalRadioButtons;
+
+    private JRadioButton[] volatileRadioButtons;
+    private JRadioButton[] transientRadioButtons;
+
+    private JRadioButton[] synchronizedRadioButtons;
+    private JRadioButton[] nativeRadioButtons;
+    private JRadioButton[] abstractRadioButtons;
+    private JRadioButton[] strictRadioButtons;
+
+    private JTextField nameTextField      = new JTextField(20);
+    private JTextField typeTextField      = new JTextField(20);
+    private JTextField argumentsTextField = new JTextField(20);
+    private int        returnValue;
+
+
+    public ClassMemberSpecificationDialog(JDialog owner, boolean isField)
+    {
+        super(owner, true);
+        setResizable(true);
+
+        // Create some constraints that can be reused.
+        GridBagConstraints constraints = new GridBagConstraints();
+        constraints.anchor = GridBagConstraints.WEST;
+        constraints.insets = new Insets(1, 2, 1, 2);
+
+        GridBagConstraints constraintsStretch = new GridBagConstraints();
+        constraintsStretch.fill    = GridBagConstraints.HORIZONTAL;
+        constraintsStretch.weightx = 1.0;
+        constraintsStretch.anchor  = GridBagConstraints.WEST;
+        constraintsStretch.insets  = constraints.insets;
+
+        GridBagConstraints constraintsLast = new GridBagConstraints();
+        constraintsLast.gridwidth = GridBagConstraints.REMAINDER;
+        constraintsLast.anchor    = GridBagConstraints.WEST;
+        constraintsLast.insets    = constraints.insets;
+
+        GridBagConstraints constraintsLastStretch = new GridBagConstraints();
+        constraintsLastStretch.gridwidth = GridBagConstraints.REMAINDER;
+        constraintsLastStretch.fill      = GridBagConstraints.HORIZONTAL;
+        constraintsLastStretch.weightx   = 1.0;
+        constraintsLastStretch.anchor    = GridBagConstraints.WEST;
+        constraintsLastStretch.insets    = constraints.insets;
+
+        GridBagConstraints panelConstraints = new GridBagConstraints();
+        panelConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        panelConstraints.fill      = GridBagConstraints.HORIZONTAL;
+        panelConstraints.weightx   = 1.0;
+        panelConstraints.weighty   = 0.0;
+        panelConstraints.anchor    = GridBagConstraints.NORTHWEST;
+        panelConstraints.insets    = constraints.insets;
+
+        GridBagConstraints stretchPanelConstraints = new GridBagConstraints();
+        stretchPanelConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        stretchPanelConstraints.fill      = GridBagConstraints.BOTH;
+        stretchPanelConstraints.weightx   = 1.0;
+        stretchPanelConstraints.weighty   = 1.0;
+        stretchPanelConstraints.anchor    = GridBagConstraints.NORTHWEST;
+        stretchPanelConstraints.insets    = constraints.insets;
+
+        GridBagConstraints labelConstraints = new GridBagConstraints();
+        labelConstraints.anchor = GridBagConstraints.CENTER;
+        labelConstraints.insets = new Insets(2, 10, 2, 10);
+
+        GridBagConstraints lastLabelConstraints = new GridBagConstraints();
+        lastLabelConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        lastLabelConstraints.anchor    = GridBagConstraints.CENTER;
+        lastLabelConstraints.insets    = labelConstraints.insets;
+
+        GridBagConstraints okButtonConstraints = new GridBagConstraints();
+        okButtonConstraints.weightx = 1.0;
+        okButtonConstraints.weighty = 1.0;
+        okButtonConstraints.anchor  = GridBagConstraints.SOUTHEAST;
+        okButtonConstraints.insets  = new Insets(4, 4, 8, 4);
+
+        GridBagConstraints cancelButtonConstraints = new GridBagConstraints();
+        cancelButtonConstraints.gridwidth = GridBagConstraints.REMAINDER;;
+        cancelButtonConstraints.weighty   = 1.0;
+        cancelButtonConstraints.anchor    = GridBagConstraints.SOUTHEAST;
+        cancelButtonConstraints.insets    = okButtonConstraints.insets;
+
+        GridBagLayout layout = new GridBagLayout();
+
+        Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
+
+        this.isField = isField;
+
+        // Create the access panel.
+        JPanel accessPanel = new JPanel(layout);
+        accessPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                               GUIResources.getMessage("access")));
+
+        accessPanel.add(Box.createGlue(),         labelConstraints);
+        accessPanel.add(new JLabel(GUIResources.getMessage("required")), labelConstraints);
+        accessPanel.add(new JLabel(GUIResources.getMessage("not")),      labelConstraints);
+        accessPanel.add(new JLabel(GUIResources.getMessage("dontCare")), labelConstraints);
+        accessPanel.add(Box.createGlue(),         constraintsLastStretch);
+
+        publicRadioButtons           = addRadioButtonTriplet("Public",       accessPanel);
+        privateRadioButtons          = addRadioButtonTriplet("Private",      accessPanel);
+        protectedRadioButtons        = addRadioButtonTriplet("Protected",    accessPanel);
+        staticRadioButtons           = addRadioButtonTriplet("Static",       accessPanel);
+        finalRadioButtons            = addRadioButtonTriplet("Final",        accessPanel);
+
+        if (isField)
+        {
+            volatileRadioButtons     = addRadioButtonTriplet("Volatile",     accessPanel);
+            transientRadioButtons    = addRadioButtonTriplet("Transient",    accessPanel);
+        }
+        else
+        {
+            synchronizedRadioButtons = addRadioButtonTriplet("Synchronized", accessPanel);
+            nativeRadioButtons       = addRadioButtonTriplet("Native",       accessPanel);
+            abstractRadioButtons     = addRadioButtonTriplet("Abstract",     accessPanel);
+            strictRadioButtons       = addRadioButtonTriplet("Strict",       accessPanel);
+        }
+
+        // Create the type panel.
+        JPanel typePanel = new JPanel(layout);
+        typePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                             GUIResources.getMessage("returnType")));
+
+        typePanel.add(typeTextField, constraintsLastStretch);
+
+        // Create the name panel.
+        JPanel namePanel = new JPanel(layout);
+        namePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                             GUIResources.getMessage("name")));
+
+        namePanel.add(nameTextField, constraintsLastStretch);
+
+        // Create the arguments panel.
+        JPanel argumentsPanel = new JPanel(layout);
+        argumentsPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                                  GUIResources.getMessage("arguments")));
+
+        argumentsPanel.add(argumentsTextField, constraintsLastStretch);
+
+        // Create the Ok button.
+        JButton okButton = new JButton(GUIResources.getMessage("ok"));
+        okButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                returnValue = APPROVE_OPTION;
+                hide();
+            }
+        });
+
+        // Create the Cancel button.
+        JButton cancelButton = new JButton(GUIResources.getMessage("cancel"));
+        cancelButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                hide();
+            }
+        });
+
+        // Add all panels to the main panel.
+        JPanel mainPanel = new JPanel(layout);
+        mainPanel.add(accessPanel, panelConstraints);
+        mainPanel.add(typePanel,   panelConstraints);
+        mainPanel.add(namePanel,   panelConstraints);
+
+        if (!isField)
+        {
+            mainPanel.add(argumentsPanel, panelConstraints);
+        }
+
+        mainPanel.add(okButton,     okButtonConstraints);
+        mainPanel.add(cancelButton, cancelButtonConstraints);
+
+        getContentPane().add(mainPanel);
+    }
+
+
+    /**
+     * Adds a JLabel and three JRadioButton instances in a ButtonGroup to the
+     * given panel with a GridBagLayout, and returns the buttons in an array.
+     */
+    private JRadioButton[] addRadioButtonTriplet(String labelText,
+                                                 JPanel panel)
+    {
+        GridBagConstraints labelConstraints = new GridBagConstraints();
+        labelConstraints.anchor = GridBagConstraints.WEST;
+        labelConstraints.insets = new Insets(2, 10, 2, 10);
+
+        GridBagConstraints buttonConstraints = new GridBagConstraints();
+        buttonConstraints.insets = labelConstraints.insets;
+
+        GridBagConstraints lastGlueConstraints = new GridBagConstraints();
+        lastGlueConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        lastGlueConstraints.weightx   = 1.0;
+
+        // Create the radio buttons.
+        JRadioButton radioButton0 = new JRadioButton();
+        JRadioButton radioButton1 = new JRadioButton();
+        JRadioButton radioButton2 = new JRadioButton();
+
+        // Put them in a button group.
+        ButtonGroup buttonGroup = new ButtonGroup();
+        buttonGroup.add(radioButton0);
+        buttonGroup.add(radioButton1);
+        buttonGroup.add(radioButton2);
+
+        // Add the label and the buttons to the panel.
+        panel.add(new JLabel(labelText), labelConstraints);
+        panel.add(radioButton0,          buttonConstraints);
+        panel.add(radioButton1,          buttonConstraints);
+        panel.add(radioButton2,          buttonConstraints);
+        panel.add(Box.createGlue(),      lastGlueConstraints);
+
+        return new JRadioButton[]
+        {
+             radioButton0,
+             radioButton1,
+             radioButton2
+        };
+    }
+
+
+    /**
+     * Sets the ClassMemberSpecification to be represented in this dialog.
+     */
+    public void setClassMemberSpecification(ClassMemberSpecification classMemberSpecification)
+    {
+        String name       = classMemberSpecification.name;
+        String descriptor = classMemberSpecification.descriptor;
+
+        // Set the access radio buttons.
+        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_PUBLIC,       publicRadioButtons);
+        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_PRIVATE,      privateRadioButtons);
+        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_PROTECTED,    protectedRadioButtons);
+        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_STATIC,       staticRadioButtons);
+        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_FINAL,        finalRadioButtons);
+        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_VOLATILE,     volatileRadioButtons);
+        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_TRANSIENT,    transientRadioButtons);
+        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_SYNCHRONIZED, synchronizedRadioButtons);
+        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_NATIVE,       nativeRadioButtons);
+        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT,     abstractRadioButtons);
+        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_STRICT,       strictRadioButtons);
+
+        // Set the class name text fields.
+        nameTextField.setText(name == null ? "" : name);
+
+        if (isField)
+        {
+            typeTextField     .setText(descriptor == null ? "" : ClassUtil.externalType(descriptor));
+        }
+        else
+        {
+            typeTextField     .setText(descriptor == null ? "" : ClassUtil.externalMethodReturnType(descriptor));
+            argumentsTextField.setText(descriptor == null ? "" : ClassUtil.externalMethodArguments(descriptor));
+        }
+    }
+
+
+    /**
+     * Returns the ClassMemberSpecification currently represented in this dialog.
+     */
+    public ClassMemberSpecification getClassMemberSpecification()
+    {
+        String  name      = nameTextField.getText();
+        String  type      = typeTextField.getText();
+        String  arguments = argumentsTextField.getText();
+
+        boolean fullWildcard = name.equals("") ||
+                               type.equals("");
+
+        ClassMemberSpecification classMemberSpecification = fullWildcard ?
+            new ClassMemberSpecification(0,
+                                         0,
+                                         null,
+                                         null) :
+            new ClassMemberSpecification(0,
+                                         0,
+                                         name,
+                                         isField ? ClassUtil.internalType(type) :
+                                                   ClassUtil.internalMethodDescriptor(type, ListUtil.commaSeparatedList(arguments)));
+
+        // Also get the access radio button settings.
+        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_PUBLIC,       publicRadioButtons);
+        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_PRIVATE,      privateRadioButtons);
+        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_PROTECTED,    protectedRadioButtons);
+        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_STATIC,       staticRadioButtons);
+        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_FINAL,        finalRadioButtons);
+        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_VOLATILE,     volatileRadioButtons);
+        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_TRANSIENT,    transientRadioButtons);
+        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_SYNCHRONIZED, synchronizedRadioButtons);
+        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_NATIVE,       nativeRadioButtons);
+        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT,     abstractRadioButtons);
+        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_STRICT,       strictRadioButtons);
+
+        return classMemberSpecification;
+    }
+
+
+    /**
+     * Shows this dialog. This method only returns when the dialog is closed.
+     *
+     * @return <code>CANCEL_OPTION</code> or <code>APPROVE_OPTION</code>,
+     *         depending on the choice of the user.
+     */
+    public int showDialog()
+    {
+        returnValue = CANCEL_OPTION;
+
+        // Open the dialog in the right place, then wait for it to be closed,
+        // one way or another.
+        pack();
+        setLocationRelativeTo(getOwner());
+        show();
+
+        return returnValue;
+    }
+
+
+    /**
+     * Sets the appropriate radio button of a given triplet, based on the access
+     * flags of the given keep option.
+     */
+    private void setClassMemberSpecificationRadioButtons(ClassMemberSpecification classMemberSpecification,
+                                                         int                   flag,
+                                                         JRadioButton[]        radioButtons)
+    {
+        if (radioButtons != null)
+        {
+            int index = (classMemberSpecification.requiredSetAccessFlags   & flag) != 0 ? 0 :
+                        (classMemberSpecification.requiredUnsetAccessFlags & flag) != 0 ? 1 :
+                                                                                       2;
+            radioButtons[index].setSelected(true);
+        }
+    }
+
+
+    /**
+     * Updates the access flag of the given keep option, based on the given radio
+     * button triplet.
+     */
+    private void getClassMemberSpecificationRadioButtons(ClassMemberSpecification classMemberSpecification,
+                                                         int                   flag,
+                                                         JRadioButton[]        radioButtons)
+    {
+        if (radioButtons != null)
+        {
+            if      (radioButtons[0].isSelected())
+            {
+                classMemberSpecification.requiredSetAccessFlags   |= flag;
+            }
+            else if (radioButtons[1].isSelected())
+            {
+                classMemberSpecification.requiredUnsetAccessFlags |= flag;
+            }
+        }
+    }
+}
diff --git a/src/proguard/gui/ClassMemberSpecificationsPanel.java b/src/proguard/gui/ClassMemberSpecificationsPanel.java
new file mode 100644
index 0000000..4d34fed
--- /dev/null
+++ b/src/proguard/gui/ClassMemberSpecificationsPanel.java
@@ -0,0 +1,268 @@
+/* $Id: ClassMemberSpecificationsPanel.java,v 1.5 2004/08/28 22:50:49 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui;
+
+import proguard.*;
+import proguard.classfile.util.ClassUtil;
+
+import java.awt.Component;
+import java.awt.event.*;
+import java.util.*;
+
+import javax.swing.*;
+
+
+/**
+ * This <code>ListPanel</code> allows the user to add, edit, move, and remove
+ * ClassMemberSpecification entries in a list.
+ *
+ * @author Eric Lafortune
+ */
+class ClassMemberSpecificationsPanel extends ListPanel
+{
+    private ClassMemberSpecificationDialog fieldSpecificationDialog;
+    private ClassMemberSpecificationDialog methodSpecificationDialog;
+
+
+    public ClassMemberSpecificationsPanel(JDialog owner, boolean fullKeepOptions)
+    {
+        super();
+
+        super.firstSelectionButton = fullKeepOptions ? 3 : 2;
+
+        list.setCellRenderer(new MyListCellRenderer());
+
+        fieldSpecificationDialog  = new ClassMemberSpecificationDialog(owner, true);
+        methodSpecificationDialog = new ClassMemberSpecificationDialog(owner, false);
+
+        if (fullKeepOptions)
+        {
+            addAddFieldButton();
+        }
+        addAddMethodButton();
+        addEditButton();
+        addRemoveButton();
+        addUpButton();
+        addDownButton();
+
+        enableSelectionButtons();
+    }
+
+
+    protected void addAddFieldButton()
+    {
+        JButton addFieldButton = new JButton(GUIResources.getMessage("addField"));
+        addFieldButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                fieldSpecificationDialog.setClassMemberSpecification(new ClassMemberSpecification());
+                int returnValue = fieldSpecificationDialog.showDialog();
+                if (returnValue == ClassMemberSpecificationDialog.APPROVE_OPTION)
+                {
+                    // Add the new element.
+                    addElement(new MyClassMemberSpecificationWrapper(fieldSpecificationDialog.getClassMemberSpecification(),
+                                                                  true));
+                }
+            }
+        });
+
+        addButton(addFieldButton);
+    }
+
+
+    protected void addAddMethodButton()
+    {
+        JButton addMethodButton = new JButton(GUIResources.getMessage("addMethod"));
+        addMethodButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                methodSpecificationDialog.setClassMemberSpecification(new ClassMemberSpecification());
+                int returnValue = methodSpecificationDialog.showDialog();
+                if (returnValue == ClassMemberSpecificationDialog.APPROVE_OPTION)
+                {
+                    // Add the new element.
+                    addElement(new MyClassMemberSpecificationWrapper(methodSpecificationDialog.getClassMemberSpecification(),
+                                                                  false));
+                }
+            }
+        });
+
+        addButton(addMethodButton);
+    }
+
+
+    protected void addEditButton()
+    {
+        JButton editButton = new JButton(GUIResources.getMessage("edit"));
+        editButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                MyClassMemberSpecificationWrapper wrapper =
+                    (MyClassMemberSpecificationWrapper)list.getSelectedValue();
+
+                ClassMemberSpecificationDialog classMemberSpecificationDialog =
+                    wrapper.isField ?
+                        fieldSpecificationDialog :
+                        methodSpecificationDialog;
+
+                classMemberSpecificationDialog.setClassMemberSpecification(wrapper.classMemberSpecification);
+                int returnValue = classMemberSpecificationDialog.showDialog();
+                if (returnValue == ClassMemberSpecificationDialog.APPROVE_OPTION)
+                {
+                    // Replace the old element.
+                    wrapper.classMemberSpecification = classMemberSpecificationDialog.getClassMemberSpecification();
+                    setElementAt(wrapper,
+                                 list.getSelectedIndex());
+                }
+            }
+        });
+
+        addButton(editButton);
+    }
+
+
+    /**
+     * Sets the ClassMemberSpecification instances to be represented in this panel.
+     */
+    public void setClassMemberSpecifications(List fieldSpecifications,
+                                             List methodSpecifications)
+    {
+        listModel.clear();
+
+        if (fieldSpecifications != null)
+        {
+            for (int index = 0; index < fieldSpecifications.size(); index++)
+            {
+                listModel.addElement(
+                    new MyClassMemberSpecificationWrapper((ClassMemberSpecification)fieldSpecifications.get(index),
+                                                          true));
+            }
+        }
+
+        if (methodSpecifications != null)
+        {
+            for (int index = 0; index < methodSpecifications.size(); index++)
+            {
+                listModel.addElement(
+                    new MyClassMemberSpecificationWrapper((ClassMemberSpecification)methodSpecifications.get(index),
+                                                          false));
+            }
+        }
+
+        // Make sure the selection buttons are properly enabled,
+        // since the clear method doesn't seem to notify the listener.
+        enableSelectionButtons();
+    }
+
+
+    /**
+     * Returns the ClassMemberSpecification instances currently represented in
+     * this panel, referring to fields or to methods.
+     *
+     * @param isField specifies whether specifications referring to fields or
+     *                specifications referring to methods should be returned.
+     */
+    public List getClassMemberSpecifications(boolean isField)
+    {
+        int size = listModel.size();
+        if (size == 0)
+        {
+            return null;
+        }
+
+        List classMemberSpecifcations = new ArrayList(size);
+        for (int index = 0; index < size; index++)
+        {
+            MyClassMemberSpecificationWrapper wrapper =
+                (MyClassMemberSpecificationWrapper)listModel.get(index);
+
+            if (wrapper.isField == isField)
+            {
+                classMemberSpecifcations.add(wrapper.classMemberSpecification);
+            }
+        }
+
+        return classMemberSpecifcations;
+    }
+
+
+    /**
+     * This ListCellRenderer renders ClassMemberSpecification objects.
+     */
+    private static class MyListCellRenderer implements ListCellRenderer
+    {
+        JLabel label = new JLabel();
+
+
+        // Implementations for ListCellRenderer.
+
+        public Component getListCellRendererComponent(JList   list,
+                                                      Object  value,
+                                                      int     index,
+                                                      boolean isSelected,
+                                                      boolean cellHasFocus)
+        {
+            MyClassMemberSpecificationWrapper wrapper = (MyClassMemberSpecificationWrapper)value;
+
+            ClassMemberSpecification option = wrapper.classMemberSpecification;
+            String name = option.name;
+            label.setText(wrapper.isField ?
+                (name == null ? "<fields>"  : ClassUtil.externalFullFieldDescription(0, option.name, option.descriptor)) :
+                (name == null ? "<methods>" : ClassUtil.externalFullMethodDescription("<init>", 0, option.name, option.descriptor)));
+
+            if (isSelected)
+            {
+                label.setBackground(list.getSelectionBackground());
+                label.setForeground(list.getSelectionForeground());
+            }
+            else
+            {
+                label.setBackground(list.getBackground());
+                label.setForeground(list.getForeground());
+            }
+
+            label.setOpaque(true);
+
+            return label;
+        }
+    }
+
+
+    /**
+     * This class wraps a ClassMemberSpecification, additionally storing whether
+     * the option refers to a field or to a method.
+     */
+    private static class MyClassMemberSpecificationWrapper
+    {
+        public ClassMemberSpecification classMemberSpecification;
+        public boolean                  isField;
+
+        public MyClassMemberSpecificationWrapper(ClassMemberSpecification classMemberSpecification,
+                                              boolean               isField)
+        {
+            this.classMemberSpecification = classMemberSpecification;
+            this.isField                  = isField;
+        }
+    }
+}
diff --git a/src/proguard/gui/ClassPathPanel.java b/src/proguard/gui/ClassPathPanel.java
new file mode 100644
index 0000000..a15d1f5
--- /dev/null
+++ b/src/proguard/gui/ClassPathPanel.java
@@ -0,0 +1,415 @@
+/* $Id: ClassPathPanel.java,v 1.14 2004/08/21 21:35:28 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.File;
+
+import javax.swing.*;
+
+import proguard.*;
+
+/**
+ * This <code>ListPanel</code> allows the user to add, edit, filter, move, and
+ * remove ClassPathEntry objects in a ClassPath object.
+ *
+ * @author Eric Lafortune
+ */
+class ClassPathPanel extends ListPanel
+{
+    private JFrame       owner;
+    private boolean      inputAndOutput;
+    private JFileChooser chooser;
+    private FilterDialog filterDialog;
+
+
+    public ClassPathPanel(JFrame owner, boolean inputAndOutput)
+    {
+        super();
+
+        super.firstSelectionButton = inputAndOutput ? 3 : 2;
+
+        this.owner          = owner;
+        this.inputAndOutput = inputAndOutput;
+
+        list.setCellRenderer(new MyListCellRenderer());
+
+        chooser = new JFileChooser("");
+        chooser.setMultiSelectionEnabled(true);
+        chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
+        chooser.addChoosableFileFilter(
+            new ExtensionFileFilter(GUIResources.getMessage("jarWarEarZipExtensions"),
+                                    new String[] { ".jar", ".war", ".ear", ".zip" }));
+        chooser.setApproveButtonText(GUIResources.getMessage("ok"));
+
+        filterDialog = new FilterDialog(owner, GUIResources.getMessage("enterFilter"));
+
+        addAddButton(inputAndOutput, false);
+        if (inputAndOutput)
+        {
+            addAddButton(inputAndOutput, true);
+        }
+        addEditButton();
+        addFilterButton();
+        addRemoveButton();
+        addUpButton();
+        addDownButton();
+
+        enableSelectionButtons();
+    }
+
+
+    protected void addAddButton(boolean       inputAndOutput,
+                                final boolean isOutput)
+    {
+        JButton addButton = new JButton(GUIResources.getMessage(inputAndOutput ?
+                                                                isOutput ? "addOutput" :
+                                                                           "addInput" :
+                                                                           "add"));
+        addButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                chooser.setDialogTitle(GUIResources.getMessage("addJars"));
+                chooser.setSelectedFile(null);
+                chooser.setSelectedFiles(null);
+
+                int returnValue = chooser.showOpenDialog(owner);
+                if (returnValue == JFileChooser.APPROVE_OPTION)
+                {
+                    File[] selectedFiles = chooser.getSelectedFiles();
+                    ClassPathEntry[] entries = classPathEntries(selectedFiles, isOutput);
+
+                    // Add the new elements.
+                    addElements(entries);
+                }
+            }
+        });
+
+        addButton(addButton);
+    }
+
+
+    protected void addEditButton()
+    {
+        JButton editButton = new JButton(GUIResources.getMessage("edit"));
+        editButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                boolean isOutput = false;
+
+                int[] selectedIndices = list.getSelectedIndices();
+
+                // Copy the Object array into a File array.
+                File[] selectedFiles = new File[selectedIndices.length];
+                for (int index = 0; index < selectedFiles.length; index++)
+                {
+                    ClassPathEntry entry =
+                        (ClassPathEntry)listModel.getElementAt(selectedIndices[index]);
+
+                    isOutput = entry.isOutput();
+
+                    selectedFiles[index] = new File(entry.getName());
+                }
+
+                chooser.setDialogTitle(GUIResources.getMessage("chooseJars"));
+
+                // Up to JDK 1.3.1, setSelectedFiles doesn't show in the file
+                // chooser, so we just use setSelectedFile first. It also sets
+                // the current directory.
+                chooser.setSelectedFile(selectedFiles[0]);
+                chooser.setSelectedFiles(selectedFiles);
+
+                int returnValue = chooser.showOpenDialog(owner);
+                if (returnValue == JFileChooser.APPROVE_OPTION)
+                {
+                    selectedFiles = chooser.getSelectedFiles();
+                    ClassPathEntry[] entries = classPathEntries(selectedFiles, isOutput);
+
+                    // If there are the same number of files selected now as
+                    // there were before, we can just replace the old ones.
+                    if (selectedIndices.length == selectedFiles.length)
+                    {
+                        // Replace the old elements.
+                        setElementsAt(entries, selectedIndices);
+                    }
+                    else
+                    {
+                        // Remove the old elements.
+                        removeElementsAt(selectedIndices);
+
+                        // Add the new elements.
+                        addElements(entries);
+                    }
+                }
+            }
+        });
+
+        addButton(editButton);
+    }
+
+
+    protected void addFilterButton()
+    {
+        JButton filterButton = new JButton(GUIResources.getMessage("filter"));
+        filterButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                if (!list.isSelectionEmpty())
+                {
+                    int[] selectedIndices = list.getSelectedIndices();
+
+                    // Put the filters of the first selected entry in the dialog.
+                    getFiltersFrom(selectedIndices[0]);
+
+                    int returnValue = filterDialog.showDialog();
+                    if (returnValue == FilterDialog.APPROVE_OPTION)
+                    {
+                        // Apply the entered filters to all selected entries.
+                        setFiltersAt(selectedIndices);
+                    }
+                }
+            }
+        });
+
+        addButton(filterButton);
+    }
+
+
+    /**
+     * Sets the ClassPath to be represented in this panel.
+     */
+    public void setClassPath(ClassPath classPath)
+    {
+        listModel.clear();
+
+        if (classPath != null)
+        {
+            for (int index = 0; index < classPath.size(); index++)
+            {
+                listModel.addElement(classPath.get(index));
+            }
+        }
+
+        // Make sure the selection buttons are properly enabled,
+        // since the clear method doesn't seem to notify the listener.
+        enableSelectionButtons();
+    }
+
+
+    /**
+     * Returns the ClassPath currently represented in this panel.
+     */
+    public ClassPath getClassPath()
+    {
+        int size = listModel.size();
+        if (size == 0)
+        {
+            return null;
+        }
+
+        ClassPath classPath = new ClassPath();
+        for (int index = 0; index < size; index++)
+        {
+            classPath.add((ClassPathEntry)listModel.get(index));
+        }
+
+        return classPath;
+    }
+
+
+    /**
+     * Converts the given array of File objects into a corresponding array of
+     * ClassPathEntry objects.
+     */
+    private ClassPathEntry[] classPathEntries(File[] files, boolean isOutput)
+    {
+        ClassPathEntry[] entries = new ClassPathEntry[files.length];
+        for (int index = 0; index < entries.length; index++)
+        {
+            entries[index] = new ClassPathEntry(files[index].toString(), isOutput);
+        }
+        return entries;
+    }
+
+
+    /**
+     * Sets up the filter dialog with the filters from the specified class path
+     * entry.
+     */
+    private void getFiltersFrom(int index)
+    {
+        ClassPathEntry firstEntry = (ClassPathEntry)listModel.get(index);
+
+        filterDialog.setFilter(firstEntry.getFilter());
+        filterDialog.setJarFilter(firstEntry.getJarFilter());
+        filterDialog.setWarFilter(firstEntry.getWarFilter());
+        filterDialog.setEarFilter(firstEntry.getEarFilter());
+        filterDialog.setZipFilter(firstEntry.getZipFilter());
+    }
+
+
+    /**
+     * Applies the entered filter to the specified class path entries.
+     * Any previously set filters are discarded.
+     */
+    private void setFiltersAt(int[] indices)
+    {
+        for (int index = indices.length - 1; index >= 0; index--)
+        {
+            ClassPathEntry entry = (ClassPathEntry)listModel.get(indices[index]);
+            entry.setFilter(filterDialog.getFilter());
+            entry.setJarFilter(filterDialog.getJarFilter());
+            entry.setWarFilter(filterDialog.getWarFilter());
+            entry.setEarFilter(filterDialog.getEarFilter());
+            entry.setZipFilter(filterDialog.getZipFilter());
+        }
+
+        // Make sure they are selected and thus repainted.
+        list.setSelectedIndices(indices);
+    }
+
+
+    /**
+     * This ListCellRenderer renders ClassPathEntry objects.
+     */
+    private class MyListCellRenderer implements ListCellRenderer
+    {
+        private static final String ARROW_IMAGE_FILE = "arrow.gif";
+
+        private JPanel cellPanel    = new JPanel(new GridBagLayout());
+        private JLabel iconLabel    = new JLabel("", JLabel.RIGHT);
+        private JLabel jarNameLabel = new JLabel("", JLabel.RIGHT);
+        private JLabel filterLabel  = new JLabel("", JLabel.RIGHT);
+
+        private Icon arrowIcon;
+
+
+        public MyListCellRenderer()
+        {
+            GridBagConstraints jarNameLabelConstraints = new GridBagConstraints();
+            jarNameLabelConstraints.anchor             = GridBagConstraints.WEST;
+            jarNameLabelConstraints.insets             = new Insets(1, 2, 1, 2);
+
+            GridBagConstraints filterLabelConstraints  = new GridBagConstraints();
+            filterLabelConstraints.gridwidth           = GridBagConstraints.REMAINDER;
+            filterLabelConstraints.fill                = GridBagConstraints.HORIZONTAL;
+            filterLabelConstraints.weightx             = 1.0;
+            filterLabelConstraints.anchor              = GridBagConstraints.EAST;
+            filterLabelConstraints.insets              = jarNameLabelConstraints.insets;
+
+            arrowIcon = new ImageIcon(Toolkit.getDefaultToolkit().getImage(this.getClass().getResource(ARROW_IMAGE_FILE)));
+
+            cellPanel.add(iconLabel,    jarNameLabelConstraints);
+            cellPanel.add(jarNameLabel, jarNameLabelConstraints);
+            cellPanel.add(filterLabel,  filterLabelConstraints);
+        }
+
+
+        // Implementations for ListCellRenderer.
+
+        public Component getListCellRendererComponent(JList   list,
+                                                      Object  value,
+                                                      int     index,
+                                                      boolean isSelected,
+                                                      boolean cellHasFocus)
+        {
+            ClassPathEntry entry = (ClassPathEntry)value;
+
+            // Prepend an arrow to the output entries.
+            if (inputAndOutput && entry.isOutput())
+            {
+                iconLabel.setIcon(arrowIcon);
+            }
+            else
+            {
+                iconLabel.setIcon(null);
+            }
+
+            // Set the entry name text.
+            jarNameLabel.setText(entry.getName());
+
+            // Set the filter text.
+            StringBuffer filter = null;
+            filter = appendFilter(filter, entry.getZipFilter());
+            filter = appendFilter(filter, entry.getEarFilter());
+            filter = appendFilter(filter, entry.getWarFilter());
+            filter = appendFilter(filter, entry.getJarFilter());
+            filter = appendFilter(filter, entry.getFilter());
+
+            if (filter != null)
+            {
+                filter.append(')');
+            }
+
+            filterLabel.setText(filter != null ? filter.toString() : "");
+
+            // Set the colors.
+            if (isSelected)
+            {
+                cellPanel.setBackground(list.getSelectionBackground());
+                jarNameLabel.setForeground(list.getSelectionForeground());
+                filterLabel.setForeground(list.getSelectionForeground());
+            }
+            else
+            {
+                cellPanel.setBackground(list.getBackground());
+                jarNameLabel.setForeground(list.getForeground());
+                filterLabel.setForeground(list.getForeground());
+            }
+
+            // Make the font color red if this is an input file that can't be read.
+            if (!(inputAndOutput && entry.isOutput()) &&
+                !new File(entry.getName()).canRead())
+            {
+                jarNameLabel.setForeground(Color.red);
+            }
+
+            cellPanel.setOpaque(true);
+
+            return cellPanel;
+        }
+
+
+        private StringBuffer appendFilter(StringBuffer filter, String additionalFilter)
+        {
+            if (filter != null)
+            {
+                filter.append(';');
+            }
+
+            if (additionalFilter != null)
+            {
+                if (filter == null)
+                {
+                    filter = new StringBuffer().append('(');
+                }
+
+                filter.append(additionalFilter);
+            }
+
+            return filter;
+        }
+    }
+}
diff --git a/src/proguard/gui/ClassSpecificationDialog.java b/src/proguard/gui/ClassSpecificationDialog.java
new file mode 100644
index 0000000..d98efb8
--- /dev/null
+++ b/src/proguard/gui/ClassSpecificationDialog.java
@@ -0,0 +1,414 @@
+/* $Id: ClassSpecificationDialog.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.border.Border;
+
+import java.util.List;
+
+import proguard.ClassSpecification;
+import proguard.classfile.ClassConstants;
+import proguard.classfile.util.ClassUtil;
+
+/**
+ * This <code>JDialog</code> allows the user to enter a String.
+ *
+ * @author Eric Lafortune
+ */
+class ClassSpecificationDialog extends JDialog
+{
+    /**
+     * Return value if the dialog is canceled (with the Cancel button or by
+     * closing the dialog window).
+     */
+    public static final int CANCEL_OPTION = 1;
+
+    /**
+     * Return value if the dialog is approved (with the Ok button).
+     */
+    public static final int APPROVE_OPTION = 0;
+
+
+    private JTextArea commentsTextArea = new JTextArea(4, 20);
+
+    private JRadioButton keepClassesAndMembersRadioButton  = new JRadioButton(GUIResources.getMessage("keep"));
+    private JRadioButton keepClassMembersRadioButton       = new JRadioButton(GUIResources.getMessage("keepClassMembers"));
+    private JRadioButton keepClassesWithMembersRadioButton = new JRadioButton(GUIResources.getMessage("keepClassesWithMembers"));
+
+    private JRadioButton[] publicRadioButtons;
+    private JRadioButton[] finalRadioButtons;
+    private JRadioButton[] interfaceRadioButtons;
+    private JRadioButton[] abstractRadioButtons;
+
+    private JTextField classNameTextField        = new JTextField(20);
+    private JTextField extendsClassNameTextField = new JTextField(20);
+
+    private ClassMemberSpecificationsPanel classMembersPanel;
+
+    private int returnValue;
+
+
+    public ClassSpecificationDialog(JFrame owner, boolean fullKeepOptions)
+    {
+        super(owner, true);
+        setResizable(true);
+
+        // Create some constraints that can be reused.
+        GridBagConstraints constraints = new GridBagConstraints();
+        constraints.anchor = GridBagConstraints.WEST;
+        constraints.insets = new Insets(1, 2, 1, 2);
+
+        GridBagConstraints constraintsStretch = new GridBagConstraints();
+        constraintsStretch.fill    = GridBagConstraints.HORIZONTAL;
+        constraintsStretch.weightx = 1.0;
+        constraintsStretch.anchor  = GridBagConstraints.WEST;
+        constraintsStretch.insets  = constraints.insets;
+
+        GridBagConstraints constraintsLast = new GridBagConstraints();
+        constraintsLast.gridwidth = GridBagConstraints.REMAINDER;
+        constraintsLast.anchor    = GridBagConstraints.WEST;
+        constraintsLast.insets    = constraints.insets;
+
+        GridBagConstraints constraintsLastStretch = new GridBagConstraints();
+        constraintsLastStretch.gridwidth = GridBagConstraints.REMAINDER;
+        constraintsLastStretch.fill      = GridBagConstraints.HORIZONTAL;
+        constraintsLastStretch.weightx   = 1.0;
+        constraintsLastStretch.anchor    = GridBagConstraints.WEST;
+        constraintsLastStretch.insets    = constraints.insets;
+
+        GridBagConstraints panelConstraints = new GridBagConstraints();
+        panelConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        panelConstraints.fill      = GridBagConstraints.HORIZONTAL;
+        panelConstraints.weightx   = 1.0;
+        panelConstraints.weighty   = 0.0;
+        panelConstraints.anchor    = GridBagConstraints.NORTHWEST;
+        panelConstraints.insets    = constraints.insets;
+
+        GridBagConstraints stretchPanelConstraints = new GridBagConstraints();
+        stretchPanelConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        stretchPanelConstraints.fill      = GridBagConstraints.BOTH;
+        stretchPanelConstraints.weightx   = 1.0;
+        stretchPanelConstraints.weighty   = 1.0;
+        stretchPanelConstraints.anchor    = GridBagConstraints.NORTHWEST;
+        stretchPanelConstraints.insets    = constraints.insets;
+
+        GridBagConstraints labelConstraints = new GridBagConstraints();
+        labelConstraints.anchor = GridBagConstraints.CENTER;
+        labelConstraints.insets = new Insets(2, 10, 2, 10);
+
+        GridBagConstraints lastLabelConstraints = new GridBagConstraints();
+        lastLabelConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        lastLabelConstraints.anchor    = GridBagConstraints.CENTER;
+        lastLabelConstraints.insets    = labelConstraints.insets;
+
+        GridBagConstraints okButtonConstraints = new GridBagConstraints();
+        okButtonConstraints.weightx = 1.0;
+        okButtonConstraints.weighty = 1.0;
+        okButtonConstraints.anchor  = GridBagConstraints.SOUTHEAST;
+        okButtonConstraints.insets  = new Insets(4, 4, 8, 4);
+
+        GridBagConstraints cancelButtonConstraints = new GridBagConstraints();
+        cancelButtonConstraints.gridwidth = GridBagConstraints.REMAINDER;;
+        cancelButtonConstraints.weighty   = 1.0;
+        cancelButtonConstraints.anchor    = GridBagConstraints.SOUTHEAST;
+        cancelButtonConstraints.insets    = okButtonConstraints.insets;
+
+        GridBagLayout layout = new GridBagLayout();
+
+        Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
+
+        // Create the comments panel.
+        JPanel commentsPanel = new JPanel(layout);
+        commentsPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                                 GUIResources.getMessage("comments")));
+
+        JScrollPane commentsScrollPane = new JScrollPane(commentsTextArea);
+        commentsScrollPane.setBorder(classNameTextField.getBorder());
+
+        commentsPanel.add(commentsScrollPane, constraintsLastStretch);
+
+        // Create the keep option panel.
+        ButtonGroup keepButtonGroup = new ButtonGroup();
+        keepButtonGroup.add(keepClassesAndMembersRadioButton);
+        keepButtonGroup.add(keepClassMembersRadioButton);
+        keepButtonGroup.add(keepClassesWithMembersRadioButton);
+
+        JPanel keepOptionPanel = new JPanel(layout);
+        keepOptionPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                                   GUIResources.getMessage("keepTitle")));
+
+        keepOptionPanel.add(keepClassesAndMembersRadioButton,  constraintsLastStretch);
+        keepOptionPanel.add(keepClassMembersRadioButton,       constraintsLastStretch);
+        keepOptionPanel.add(keepClassesWithMembersRadioButton, constraintsLastStretch);
+
+        // Create the access panel.
+        JPanel accessPanel = new JPanel(layout);
+        accessPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                               GUIResources.getMessage("access")));
+
+        accessPanel.add(Box.createGlue(),         labelConstraints);
+        accessPanel.add(new JLabel(GUIResources.getMessage("required")),   labelConstraints);
+        accessPanel.add(new JLabel(GUIResources.getMessage("not")),        labelConstraints);
+        accessPanel.add(new JLabel(GUIResources.getMessage("dontCare")), labelConstraints);
+        accessPanel.add(Box.createGlue(),         constraintsLastStretch);
+
+        publicRadioButtons    = addRadioButtonTriplet("Public",    accessPanel);
+        finalRadioButtons     = addRadioButtonTriplet("Final",     accessPanel);
+        interfaceRadioButtons = addRadioButtonTriplet("Interface", accessPanel);
+        abstractRadioButtons  = addRadioButtonTriplet("Abstract",  accessPanel);
+
+        // Create the class name panel.
+        JPanel classNamePanel = new JPanel(layout);
+        classNamePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                                  GUIResources.getMessage("class")));
+
+        classNamePanel.add(classNameTextField, constraintsLastStretch);
+
+        // Create the extends class name panel.
+        JPanel extendsClassNamePanel = new JPanel(layout);
+        extendsClassNamePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                                         GUIResources.getMessage("extendsImplementsClass")));
+
+        extendsClassNamePanel.add(extendsClassNameTextField, constraintsLastStretch);
+
+
+        // Create the class member list panel.
+        classMembersPanel = new ClassMemberSpecificationsPanel(this, fullKeepOptions);
+        classMembersPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                                     GUIResources.getMessage("classMembers")));
+
+        // Create the Ok button.
+        JButton okButton = new JButton(GUIResources.getMessage("ok"));
+        okButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                returnValue = APPROVE_OPTION;
+                hide();
+            }
+        });
+
+        // Create the Cancel button.
+        JButton cancelButton = new JButton(GUIResources.getMessage("cancel"));
+        cancelButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                hide();
+            }
+        });
+
+        // Add all panels to the main panel.
+        JPanel mainPanel = new JPanel(layout);
+        mainPanel.add(commentsPanel,         panelConstraints);
+        if (fullKeepOptions)
+        {
+            mainPanel.add(keepOptionPanel,       panelConstraints);
+        }
+        mainPanel.add(accessPanel,           panelConstraints);
+        mainPanel.add(classNamePanel,        panelConstraints);
+        mainPanel.add(extendsClassNamePanel, panelConstraints);
+        mainPanel.add(classMembersPanel,     stretchPanelConstraints);
+
+        mainPanel.add(okButton,              okButtonConstraints);
+        mainPanel.add(cancelButton,          cancelButtonConstraints);
+
+        getContentPane().add(mainPanel);
+    }
+
+
+    /**
+     * Adds a JLabel and three JRadioButton instances in a ButtonGroup to the
+     * given panel with a GridBagLayout, and returns the buttons in an array.
+     */
+    private JRadioButton[] addRadioButtonTriplet(String labelText,
+                                                 JPanel panel)
+    {
+        GridBagConstraints labelConstraints = new GridBagConstraints();
+        labelConstraints.anchor = GridBagConstraints.WEST;
+        labelConstraints.insets = new Insets(2, 10, 2, 10);
+
+        GridBagConstraints buttonConstraints = new GridBagConstraints();
+        buttonConstraints.insets = labelConstraints.insets;
+
+        GridBagConstraints lastGlueConstraints = new GridBagConstraints();
+        lastGlueConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        lastGlueConstraints.weightx   = 1.0;
+
+        // Create the radio buttons.
+        JRadioButton radioButton0 = new JRadioButton();
+        JRadioButton radioButton1 = new JRadioButton();
+        JRadioButton radioButton2 = new JRadioButton();
+
+        // Put them in a button group.
+        ButtonGroup buttonGroup = new ButtonGroup();
+        buttonGroup.add(radioButton0);
+        buttonGroup.add(radioButton1);
+        buttonGroup.add(radioButton2);
+
+        // Add the label and the buttons to the panel.
+        panel.add(new JLabel(labelText), labelConstraints);
+        panel.add(radioButton0,          buttonConstraints);
+        panel.add(radioButton1,          buttonConstraints);
+        panel.add(radioButton2,          buttonConstraints);
+        panel.add(Box.createGlue(),      lastGlueConstraints);
+
+        return new JRadioButton[]
+        {
+             radioButton0,
+             radioButton1,
+             radioButton2
+        };
+    }
+
+
+    /**
+     * Sets the ClassSpecification to be represented in this dialog.
+     */
+    public void setClassSpecification(ClassSpecification classSpecification)
+    {
+        String  className         = classSpecification.className;
+        String  extendsClassName  = classSpecification.extendsClassName;
+        boolean markClassFiles    = classSpecification.markClassFiles;
+        boolean markConditionally = classSpecification.markConditionally;
+        String  comments          = classSpecification.comments;
+        List    keepFieldOptions  = classSpecification.fieldSpecifications;
+        List    keepMethodOptions = classSpecification.methodSpecifications;
+
+        // Set the comments text area.
+        commentsTextArea.setText(comments == null ? "" : comments);
+
+        // Figure out the proper keep radio button and set it.
+        JRadioButton keepOptionRadioButton =
+            markConditionally ? keepClassesWithMembersRadioButton :
+            markClassFiles    ? keepClassesAndMembersRadioButton  :
+                                keepClassMembersRadioButton;
+
+        keepOptionRadioButton.setSelected(true);
+
+        // Set the access radio buttons.
+        setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_PUBLIC,    publicRadioButtons);
+        setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_FINAL,     finalRadioButtons);
+        setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_INTERFACE, interfaceRadioButtons);
+        setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT,  abstractRadioButtons);
+
+        // Set the class name text fields.
+        classNameTextField       .setText(className        == null ? "*" : ClassUtil.externalClassName(className));
+        extendsClassNameTextField.setText(extendsClassName == null ? ""  : ClassUtil.externalClassName(extendsClassName));
+
+        // Set the keep class member option list.
+        classMembersPanel.setClassMemberSpecifications(keepFieldOptions, keepMethodOptions);
+    }
+
+
+    /**
+     * Returns the ClassSpecification currently represented in this dialog.
+     */
+    public ClassSpecification getClassSpecification()
+    {
+        String  comments          = commentsTextArea.getText();
+        String  className         = classNameTextField.getText();
+        String  extendsClassName  = extendsClassNameTextField.getText();
+        boolean markClassFiles    = !keepClassMembersRadioButton.isSelected();
+        boolean markConditionally = keepClassesWithMembersRadioButton.isSelected();
+
+        ClassSpecification classSpecification =
+            new ClassSpecification(0,
+                                    0,
+                                    className.equals("") ||
+                                    className.equals("*")       ? null : ClassUtil.internalClassName(className),
+                                    extendsClassName.equals("") ? null : ClassUtil.internalClassName(extendsClassName),
+                                    markClassFiles,
+                                    markConditionally,
+                                    comments.equals("")         ? null : comments);
+
+        // Also get the access radio button settings.
+        getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_PUBLIC,    publicRadioButtons);
+        getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_FINAL,     finalRadioButtons);
+        getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_INTERFACE, interfaceRadioButtons);
+        getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT,  abstractRadioButtons);
+
+        // Get the keep class member option lists.
+        classSpecification.fieldSpecifications  = classMembersPanel.getClassMemberSpecifications(true);
+        classSpecification.methodSpecifications = classMembersPanel.getClassMemberSpecifications(false);
+
+        return classSpecification;
+    }
+
+
+    /**
+     * Shows this dialog. This method only returns when the dialog is closed.
+     *
+     * @return <code>CANCEL_OPTION</code> or <code>APPROVE_OPTION</code>,
+     *         depending on the choice of the user.
+     */
+    public int showDialog()
+    {
+        returnValue = CANCEL_OPTION;
+
+        // Open the dialog in the right place, then wait for it to be closed,
+        // one way or another.
+        pack();
+        setLocationRelativeTo(getOwner());
+        show();
+
+        return returnValue;
+    }
+
+
+    /**
+     * Sets the appropriate radio button of a given triplet, based on the access
+     * flags of the given keep option.
+     */
+    private void setClassSpecificationRadioButtons(ClassSpecification classSpecification,
+                                                    int                 flag,
+                                                    JRadioButton[]      radioButtons)
+    {
+        int index = (classSpecification.requiredSetAccessFlags   & flag) != 0 ? 0 :
+                    (classSpecification.requiredUnsetAccessFlags & flag) != 0 ? 1 :
+                                                                                 2;
+        radioButtons[index].setSelected(true);
+    }
+
+
+    /**
+     * Updates the access flag of the given keep option, based on the given radio
+     * button triplet.
+     */
+    private void getClassSpecificationRadioButtons(ClassSpecification classSpecification,
+                                                    int                 flag,
+                                                    JRadioButton[]      radioButtons)
+    {
+        if      (radioButtons[0].isSelected())
+        {
+            classSpecification.requiredSetAccessFlags   |= flag;
+        }
+        else if (radioButtons[1].isSelected())
+        {
+            classSpecification.requiredUnsetAccessFlags |= flag;
+        }
+    }
+}
diff --git a/src/proguard/gui/ClassSpecificationsPanel.java b/src/proguard/gui/ClassSpecificationsPanel.java
new file mode 100644
index 0000000..0746ab1
--- /dev/null
+++ b/src/proguard/gui/ClassSpecificationsPanel.java
@@ -0,0 +1,191 @@
+/* $Id: ClassSpecificationsPanel.java,v 1.4 2004/08/28 22:50:49 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui;
+
+import proguard.*;
+import proguard.classfile.util.ClassUtil;
+
+import java.awt.Component;
+import java.awt.event.*;
+import java.util.*;
+
+import javax.swing.*;
+
+
+/**
+ * This <code>ListPanel</code> allows the user to add, edit, move, and remove
+ * ClassSpecification entries in a list.
+ *
+ * @author Eric Lafortune
+ */
+class ClassSpecificationsPanel extends ListPanel
+{
+    private ClassSpecificationDialog classSpecificationDialog;
+
+
+    public ClassSpecificationsPanel(JFrame owner, boolean fullKeepOptions)
+    {
+        super();
+
+        list.setCellRenderer(new MyListCellRenderer());
+
+        classSpecificationDialog = new ClassSpecificationDialog(owner, fullKeepOptions);
+
+        addAddButton();
+        addEditButton();
+        addRemoveButton();
+        addUpButton();
+        addDownButton();
+
+        enableSelectionButtons();
+    }
+
+
+    protected void addAddButton()
+    {
+        JButton addButton = new JButton(GUIResources.getMessage("add"));
+        addButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                classSpecificationDialog.setClassSpecification(new ClassSpecification());
+                int returnValue = classSpecificationDialog.showDialog();
+                if (returnValue == ClassSpecificationDialog.APPROVE_OPTION)
+                {
+                    // Add the new element.
+                    addElement(classSpecificationDialog.getClassSpecification());
+                }
+            }
+        });
+
+        addButton(addButton);
+    }
+
+
+    protected void addEditButton()
+    {
+        JButton editButton = new JButton(GUIResources.getMessage("edit"));
+        editButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                ClassSpecification selectedClassSpecification =
+                    (ClassSpecification)list.getSelectedValue();
+
+                classSpecificationDialog.setClassSpecification(selectedClassSpecification);
+                int returnValue = classSpecificationDialog.showDialog();
+                if (returnValue == ClassSpecificationDialog.APPROVE_OPTION)
+                {
+                    // Replace the old element.
+                    setElementAt(classSpecificationDialog.getClassSpecification(),
+                                 list.getSelectedIndex());
+                }
+            }
+        });
+
+        addButton(editButton);
+    }
+
+
+    /**
+     * Sets the ClassSpecification objects to be represented in this panel.
+     */
+    public void setClassSpecifications(List classSpecifications)
+    {
+        listModel.clear();
+
+        if (classSpecifications != null)
+        {
+            for (int index = 0; index < classSpecifications.size(); index++)
+            {
+                listModel.addElement(classSpecifications.get(index));
+            }
+        }
+
+        // Make sure the selection buttons are properly enabled,
+        // since the clear method doesn't seem to notify the listener.
+        enableSelectionButtons();
+    }
+
+
+    /**
+     * Returns the ClassSpecification objects currently represented in this panel.
+     */
+    public List getClassSpecifications()
+    {
+        int size = listModel.size();
+        if (size == 0)
+        {
+            return null;
+        }
+
+        List classSpecifications = new ArrayList(size);
+        for (int index = 0; index < size; index++)
+        {
+            classSpecifications.add(listModel.get(index));
+        }
+
+        return classSpecifications;
+    }
+
+
+    /**
+     * This ListCellRenderer renders ClassSpecification objects.
+     */
+    private static class MyListCellRenderer implements ListCellRenderer
+    {
+        JLabel label = new JLabel();
+
+
+        // Implementations for ListCellRenderer.
+
+        public Component getListCellRendererComponent(JList   list,
+                                                      Object  value,
+                                                      int     index,
+                                                      boolean isSelected,
+                                                      boolean cellHasFocus)
+        {
+            ClassSpecification option = (ClassSpecification)value;
+
+            String comments = option.comments;
+
+            label.setText(comments                 != null ? comments.trim()                                                                                        :
+                          option.className         != null ? (GUIResources.getMessage("class") + ' ' + ClassUtil.externalClassName(option.className))               :
+                          option.extendsClassName  != null ? (GUIResources.getMessage("extensionsOf") + ' ' + ClassUtil.externalClassName(option.extendsClassName)) :
+                                                             (GUIResources.getMessage("specificationNumber") + index));
+
+            if (isSelected)
+            {
+                label.setBackground(list.getSelectionBackground());
+                label.setForeground(list.getSelectionForeground());
+            }
+            else
+            {
+                label.setBackground(list.getBackground());
+                label.setForeground(list.getForeground());
+            }
+
+            label.setOpaque(true);
+
+            return label;
+        }
+    }
+}
diff --git a/src/proguard/gui/ExtensionFileFilter.java b/src/proguard/gui/ExtensionFileFilter.java
new file mode 100644
index 0000000..9a69355
--- /dev/null
+++ b/src/proguard/gui/ExtensionFileFilter.java
@@ -0,0 +1,78 @@
+/* $Id: ExtensionFileFilter.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui;
+
+import java.io.File;
+import javax.swing.filechooser.FileFilter;
+
+
+/**
+ * This <code>FileFilter</code> accepts files that end in one of the given
+ * extensions.
+ *
+ * @author Eric Lafortune
+ */
+class ExtensionFileFilter extends FileFilter
+{
+    private String   description;
+    private String[] extensions;
+
+
+    /**
+     * Creates a new ExtensionFileFilter.
+     * @param description a description of the filter.
+     * @param extensions  an array of acceptable extensions.
+     */
+    public ExtensionFileFilter(String description, String[] extensions)
+    {
+        this.description = description;
+        this.extensions  = extensions;
+    }
+
+
+    // Implemntations for FileFilter
+
+    public String getDescription()
+    {
+        return description;
+    }
+
+
+    public boolean accept(File file)
+    {
+        if (file.isDirectory())
+        {
+            return true;
+        }
+
+        String fileName = file.getName().toLowerCase();
+
+        for (int index = 0; index < extensions.length; index++)
+        {
+            if (fileName.endsWith(extensions[index]))
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/src/proguard/gui/FilterDialog.java b/src/proguard/gui/FilterDialog.java
new file mode 100644
index 0000000..8d2d791
--- /dev/null
+++ b/src/proguard/gui/FilterDialog.java
@@ -0,0 +1,296 @@
+/* $Id: FilterDialog.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+import javax.swing.border.*;
+
+/**
+ * This <code>JDialog</code> allows the user to enter a String.
+ *
+ * @author Eric Lafortune
+ */
+public class FilterDialog extends JDialog
+{
+    /**
+     * Return value if the dialog is canceled (with the Cancel button or by
+     * closing the dialog window).
+     */
+    public static final int CANCEL_OPTION = 1;
+
+    /**
+     * Return value if the dialog is approved (with the Ok button).
+     */
+    public static final int APPROVE_OPTION = 0;
+
+    private static final String DEFAULT_FILTER     = "**";
+    private static final String DEFAULT_JAR_FILTER = "**.jar";
+    private static final String DEFAULT_WAR_FILTER = "**.war";
+    private static final String DEFAULT_EAR_FILTER = "**.ear";
+    private static final String DEFAULT_ZIP_FILTER = "**.zip";
+
+
+    private JTextField filterTextField    = new JTextField(40);
+    private JTextField jarFilterTextField = new JTextField(40);
+    private JTextField warFilterTextField = new JTextField(40);
+    private JTextField earFilterTextField = new JTextField(40);
+    private JTextField zipFilterTextField = new JTextField(40);
+    private int        returnValue;
+
+
+    public FilterDialog(JFrame owner,
+                        String explanation)
+    {
+        super(owner, true);
+        setResizable(true);
+
+        // Create some constraints that can be reused.
+        GridBagConstraints textConstraints = new GridBagConstraints();
+        textConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        textConstraints.fill      = GridBagConstraints.HORIZONTAL;
+        textConstraints.weightx   = 1.0;
+        textConstraints.weighty   = 1.0;
+        textConstraints.anchor    = GridBagConstraints.NORTHWEST;
+        textConstraints.insets    = new Insets(10, 10, 10, 10);
+
+        GridBagConstraints labelConstraints = new GridBagConstraints();
+        labelConstraints.anchor = GridBagConstraints.WEST;
+        labelConstraints.insets = new Insets(1, 2, 1, 2);
+
+        GridBagConstraints textFieldConstraints = new GridBagConstraints();
+        textFieldConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        textFieldConstraints.fill      = GridBagConstraints.HORIZONTAL;
+        textFieldConstraints.weightx   = 1.0;
+        textFieldConstraints.anchor    = GridBagConstraints.WEST;
+        textFieldConstraints.insets    = labelConstraints.insets;
+
+        GridBagConstraints panelConstraints = new GridBagConstraints();
+        panelConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        panelConstraints.fill      = GridBagConstraints.HORIZONTAL;
+        panelConstraints.weightx   = 1.0;
+        panelConstraints.weighty   = 0.0;
+        panelConstraints.anchor    = GridBagConstraints.NORTHWEST;
+        panelConstraints.insets    = labelConstraints.insets;
+
+        GridBagConstraints okButtonConstraints = new GridBagConstraints();
+        okButtonConstraints.weightx = 1.0;
+        okButtonConstraints.weighty = 1.0;
+        okButtonConstraints.anchor  = GridBagConstraints.SOUTHEAST;
+        okButtonConstraints.insets  = new Insets(4, 4, 8, 4);
+
+        GridBagConstraints cancelButtonConstraints = new GridBagConstraints();
+        cancelButtonConstraints.gridwidth = GridBagConstraints.REMAINDER;;
+        cancelButtonConstraints.weighty   = 1.0;
+        cancelButtonConstraints.anchor    = GridBagConstraints.SOUTHEAST;
+        cancelButtonConstraints.insets    = okButtonConstraints.insets;
+
+        GridBagLayout layout = new GridBagLayout();
+
+        Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
+
+        // Create the panel with the explanation.
+        JTextArea explanationTextArea = new JTextArea(explanation, 3, 0);
+        explanationTextArea.setOpaque(false);
+        explanationTextArea.setEditable(false);
+        explanationTextArea.setLineWrap(true);
+        explanationTextArea.setWrapStyleWord(true);
+
+        // Create the filter labels.
+        JLabel filterLabel    = new JLabel(GUIResources.getMessage("nameFilter"));
+        JLabel jarFilterLabel = new JLabel(GUIResources.getMessage("jarNameFilter"));
+        JLabel warFilterLabel = new JLabel(GUIResources.getMessage("warNameFilter"));
+        JLabel earFilterLabel = new JLabel(GUIResources.getMessage("earNameFilter"));
+        JLabel zipFilterLabel = new JLabel(GUIResources.getMessage("zipNameFilter"));
+
+        // Create the filter panel.
+        JPanel filterPanel = new JPanel(layout);
+        filterPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                               GUIResources.getMessage("filters")));
+
+        filterPanel.add(explanationTextArea, textConstraints);
+
+        filterPanel.add(filterLabel,         labelConstraints);
+        filterPanel.add(filterTextField,     textFieldConstraints);
+
+        filterPanel.add(jarFilterLabel,      labelConstraints);
+        filterPanel.add(jarFilterTextField,  textFieldConstraints);
+
+        filterPanel.add(warFilterLabel,      labelConstraints);
+        filterPanel.add(warFilterTextField,  textFieldConstraints);
+
+        filterPanel.add(earFilterLabel,      labelConstraints);
+        filterPanel.add(earFilterTextField,  textFieldConstraints);
+
+        filterPanel.add(zipFilterLabel,      labelConstraints);
+        filterPanel.add(zipFilterTextField,  textFieldConstraints);
+
+
+        JButton okButton = new JButton(GUIResources.getMessage("ok"));
+        okButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                returnValue = APPROVE_OPTION;
+                hide();
+            }
+        });
+
+        JButton cancelButton = new JButton(GUIResources.getMessage("cancel"));
+        cancelButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                hide();
+            }
+        });
+
+        // Add all panels to the main panel.
+        JPanel mainPanel = new JPanel(layout);
+        mainPanel.add(filterPanel,  panelConstraints);
+        mainPanel.add(okButton,     okButtonConstraints);
+        mainPanel.add(cancelButton, cancelButtonConstraints);
+
+        getContentPane().add(mainPanel);
+    }
+
+
+    /**
+     * Sets the filter to be represented in this dialog.
+     */
+    public void setFilter(String filter)
+    {
+        filterTextField.setText(filter != null ? filter : DEFAULT_FILTER);
+    }
+
+
+    /**
+     * Returns the filter currently represented in this dialog.
+     */
+    public String getFilter()
+    {
+        String filter = filterTextField.getText();
+
+        return filter.equals(DEFAULT_FILTER) ? null : filter;
+    }
+
+
+    /**
+     * Sets the jar filter to be represented in this dialog.
+     */
+    public void setJarFilter(String filter)
+    {
+        jarFilterTextField.setText(filter != null ? filter : DEFAULT_JAR_FILTER);
+    }
+
+
+    /**
+     * Returns the jar filter currently represented in this dialog.
+     */
+    public String getJarFilter()
+    {
+        String filter = jarFilterTextField.getText();
+
+        return filter.equals(DEFAULT_JAR_FILTER) ? null : filter;
+    }
+
+
+    /**
+     * Sets the war filter to be represented in this dialog.
+     */
+    public void setWarFilter(String filter)
+    {
+        warFilterTextField.setText(filter != null ? filter : DEFAULT_WAR_FILTER);
+    }
+
+
+    /**
+     * Returns the war filter currently represented in this dialog.
+     */
+    public String getWarFilter()
+    {
+        String filter = warFilterTextField.getText();
+
+        return filter.equals(DEFAULT_WAR_FILTER) ? null : filter;
+    }
+
+
+    /**
+     * Sets the ear filter to be represented in this dialog.
+     */
+    public void setEarFilter(String filter)
+    {
+        earFilterTextField.setText(filter != null ? filter : DEFAULT_EAR_FILTER);
+    }
+
+
+    /**
+     * Returns the ear filter currently represented in this dialog.
+     */
+    public String getEarFilter()
+    {
+        String filter = earFilterTextField.getText();
+
+        return filter.equals(DEFAULT_EAR_FILTER) ? null : filter;
+    }
+
+
+    /**
+     * Sets the zip filter to be represented in this dialog.
+     */
+    public void setZipFilter(String filter)
+    {
+        zipFilterTextField.setText(filter != null ? filter : DEFAULT_ZIP_FILTER);
+    }
+
+
+    /**
+     * Returns the zip filter currently represented in this dialog.
+     */
+    public String getZipFilter()
+    {
+        String filter = zipFilterTextField.getText();
+
+        return filter.equals(DEFAULT_ZIP_FILTER) ? null : filter;
+    }
+
+
+    /**
+     * Shows this dialog. This method only returns when the dialog is closed.
+     *
+     * @return <code>CANCEL_OPTION</code> or <code>APPROVE_OPTION</code>,
+     *         depending on the choice of the user.
+     */
+    public int showDialog()
+    {
+        returnValue = CANCEL_OPTION;
+
+        // Open the dialog in the right place, then wait for it to be closed,
+        // one way or another.
+        pack();
+        setLocationRelativeTo(getOwner());
+        show();
+
+        return returnValue;
+    }
+}
diff --git a/src/proguard/gui/GUIResources.java b/src/proguard/gui/GUIResources.java
new file mode 100644
index 0000000..fa126d5
--- /dev/null
+++ b/src/proguard/gui/GUIResources.java
@@ -0,0 +1,56 @@
+/* $Id: GUIResources.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui;
+
+import java.text.MessageFormat;
+import java.util.*;
+
+
+/**
+ * This class provides some utility methods for working with resource bundles.
+ *
+ * @author Eric Lafortune
+ */
+class GUIResources
+{
+    private static ResourceBundle messages  = ResourceBundle.getBundle(new GUIResources().getClass().getName());
+    private static MessageFormat  formatter = new MessageFormat("");
+
+
+    /**
+     * Returns an internationalized message, based on its key.
+     */
+    public static String getMessage(String messageKey)
+    {
+        return messages.getString(messageKey);
+    }
+
+
+    /**
+     * Returns an internationalized, formatted message, based on its key, with
+     * the given arguments.
+     */
+    public static String getMessage(String messageKey, Object[] messageArguments)
+    {
+        formatter.applyPattern(messages.getString(messageKey));
+        return formatter.format(messageArguments);
+    }
+}
diff --git a/src/proguard/gui/GUIResources.properties b/src/proguard/gui/GUIResources.properties
new file mode 100644
index 0000000..fe0ca28
--- /dev/null
+++ b/src/proguard/gui/GUIResources.properties
@@ -0,0 +1,255 @@
+# ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+# Copyright (c) 1999-2004 Eric Lafortune (eric at graphics.cornell.edu)
+
+#
+# Tab names.
+#
+proGuardTab     = ProGuard
+inputOutputTab  = Input/Output
+shrinkingTab    = Shrinking
+obfuscationTab  = Obfuscation
+optimizationTab = Optimization
+informationTab  = Information
+processTab      = Process
+reTraceTab      = ReTrace
+
+#
+# Splash text.
+#
+developed    = Developed by Eric Lafortune
+shrinking    = Shrinking
+optimization = Optimization
+obfuscation  = Obfuscation
+
+#
+# Panel titles.
+#
+welcome                       = Welcome to ProGuard, version 3.2
+options                       = Options
+keepAdditional                = Keep additional classes and class members
+keepNamesAdditional           = Keep additional class names and class member names
+assumeNoSideEffectsAdditional = Assume no side effects for additional methods
+consistencyAndCorrectness     = Consistency and correctness
+processingConsole             = Processing console
+reTraceSettings               = ReTrace settings
+
+mappingFile            = Mapping file
+obfuscatedStackTrace   = Obfuscated stack trace
+deobfuscatedStackTrace = De-obfuscated stack trace
+
+#
+# Info texts.
+#
+proGuardInfo = \
+  ProGuard is a free class file shrinker, optimizer, and obfuscator.\
+  \n\n\
+  With this GUI, you can create, load, modify, and save ProGuard configurations. \
+  \n\
+  You can then process your code right away, or you can run ProGuard from the \
+  command line using your saved configuration. \
+  \n\n\
+  With the ReTrace part of this GUI you can de-obfuscate your stack traces.\
+  \n\n\
+  ProGuard and ReTrace are written and maintained by Eric Lafortune.\
+  \n\n\
+  Based on class file IO code by Mark Welsh.\
+  \n\n\
+  Distributed under the GNU General Public License.\
+  \n\
+  Copyright (c) 1999-2004.
+
+processingInfo = \
+  You can now start processing your code, \
+  or you can run ProGuard from the command line using your saved configuration.\
+  \n\n\
+  It's always a good idea to save your configuration first.
+
+reTraceInfo = \
+  If you had ProGuard write out a mapping file, \
+  you can de-obfuscate your obfuscated stack traces with ReTrace!\
+  \n\n\
+  You can load an obfuscated stack trace from a file, \
+  or you can paste it straight into the text area above.
+
+#
+# Titles and labels corresponding to common ProGuard options.
+#
+programJars = Program jars, wars, ears, zips, and directories
+libraryJars = Library jars, wars, ears, zips, and directories
+outputJars  = Output jars, wars, ears, zips, and directories
+
+printSeeds                       = Print seeds
+shrink                           = Shrink
+printUsage                       = Print usage
+
+optimize                         = Optimize
+allowAccessModification          = Allow access modification
+
+obfuscate                        = Obfuscate
+printMapping                     = Print mapping
+applyMapping                     = Apply mapping
+obfuscationDictionary            = Obfuscation dictionary
+overloadAggressively             = Overload aggressively
+defaultPackage                   = Default package
+useMixedCaseClassNames           = Use mixed-case class names
+keepAttributes                   = Keep attributes
+renameSourceFileAttribute        = Rename SourceFile attribute
+
+verbose                          = Verbose
+note                             = Note Class.forName invocations with variable arguments
+warn                             = Warn about missing libraries
+ignoreWarnings                   = Ignore warnings about missing libraries
+skipNonPublicLibraryClasses      = Skip non-public library classes
+skipNonPublicLibraryClassMembers = Skip non-public library class members
+
+#
+# Panel titles and labels for boilerplate keep options.
+#
+boilerplate_keep                = Keep
+boilerplate_applications        = Applications
+boilerplate_applets             = Applets
+boilerplate_servlets            = Servlets
+boilerplate_midlets             = Midlets
+boilerplate_library             = Library
+
+boilerplate_also_keep           = Also keep
+boilerplate_enumerations        = Enumerations
+boilerplate_serialization_code  = Serialization code
+boilerplate_beaninfo_classes    = BeanInfo classes
+boilerplate_bean_classes        = Bean classes
+boilerplate_rmi_interfaces      = RMI interfaces
+boilerplate_rmi_implementations = RMI implementations
+
+#
+# Panel titles and labels for boilerplate keep names options.
+#
+boilerplate_keep_names          = Keep names
+boilerplate_native_method_names = Native method names
+boilerplate__class_method_names = .class method names
+
+#
+# Labels for boilerplate "no side effect methods" options.
+#
+boilerplate_remove                          = Remove
+boilerplate_system_method_calls             = System method calls without side effects
+boilerplate_math_method_calls               = Math method calls without side effects
+boilerplate_string_method_calls             = String method calls without side effects
+boilerplate_stringbuffer_method_calls       = StringBuffer method calls without side effects
+boilerplate_stringbuilder_method_calls      = StringBuilder method calls without side effects
+
+boilerplate_remove_debugging                = Remove debugging
+boilerplate_throwable_printstacktrace_calls = Throwable.printStackTrace() calls
+boilerplate_thread_dumpstack_calls          = Thread.dumpStack() calls
+boilerplate_all_logging_api_calls           = All logging API calls
+boilerplate_all_log4j_api_calls             = All Log4j API calls
+
+#
+# Titles and labels corresponding to ProGuard keep options.
+#
+keepTitle = Keep
+
+keep                   = Keep classes and class members
+keepClassMembers       = Keep class members only
+keepClassesWithMembers = Keep classes and class members, if members are present
+
+#
+# Further keep titles and labels.
+#
+comments               = Comments
+access                 = Access
+required               = Required
+not                    = Not
+dontCare               = Don't care
+class                  = Class
+extendsImplementsClass = Extends/implements class
+classMembers           = Class members
+
+extensionsOf = Extensions of
+specificationNumber = Specification #
+
+returnType = Return type
+name       = Name
+arguments  = Arguments
+
+#
+# File selection titles.
+#
+selectConfigurationFile         = Select a configuration file...
+saveConfigurationFile           = Save configuration...
+selectSeedsFile                 = Select a seeds output file...
+selectUsageFile                 = Select a usage output file...
+selectPrintMappingFile          = Select an output mapping file...
+selectApplyMappingFile          = Select an input mapping file...
+selectObfuscationDictionaryFile = Select an obfuscation dictionary...
+selectStackTraceFile            = Select a stack trace file...
+
+cantOpenConfigurationFile  = Can''t open the configuration file [{0}]
+cantParseConfigurationFile = Can''t parse the configuration file [{0}]
+cantSaveConfigurationFile  = Can''t save the configuration file [{0}]
+cantOpenStackTraceFile     = Can''t open the stack trace file [{0}]
+
+jarWarEarZipExtensions = *.jar, *.war, *.ear, *.zip (archives and directories)
+proExtension           = *.pro (ProGuard configurations)
+
+addJars     = Add one or more jars or directories...
+chooseJars  = Choose different jars or directories...
+enterFilter = \
+  You can enter optional filters for the file names contained in the selected entries. \
+  The filters are comma-separated lists of relative file names, supporting ?, * and ** wildcards, and ! negators. \
+
+filters       = Filters
+nameFilter    = File name filter
+jarNameFilter = Jar name filter
+warNameFilter = War name filter
+earNameFilter = Ear name filter
+zipNameFilter = Zip name filter
+
+#
+# Simple button texts.
+#
+previous = Previous
+next     = Next
+browse   = Browse...
+ok       = Ok
+cancel   = Cancel
+
+add       = Add...
+addInput  = Add input...
+addOutput = Add output...
+edit      = Edit...
+filter    = Filter...
+remove    = Remove
+moveUp    = Move up
+moveDown  = Move down
+
+moveToLibraries = Move to libraries
+moveToProgram   = Move to program
+
+addField  = Add field...
+addMethod = Add method...
+
+loadConfiguration = Load configuration...
+viewConfiguration = View configuration
+saveConfiguration = Save configuration...
+loadStackTrace    = Load stack trace...
+process           = Process!
+reTrace           = ReTrace!
+
+#
+# Progress messages and error messages.
+#
+warning         = Warning
+outOfMemory     = Out of memory
+outOfMemoryInfo = \n\
+  You should run the ProGuard GUI with a larger java heap size, \
+  with a command like\
+  \n\n\t\
+  java -Xms128m -Xmx192m -jar proguardgui.jar {0}\
+  \n\n\
+  or you can try running ProGuard from the command line. \
+  with a command like\
+  \n\n\t\
+  java -jar proguard.jar @{0}
+sampleConfigurationFileName = configuration.pro
+errorProcessing = Error during processing
+errorReTracing  = Error during retracing
diff --git a/src/proguard/gui/ListPanel.java b/src/proguard/gui/ListPanel.java
new file mode 100644
index 0000000..8f2021f
--- /dev/null
+++ b/src/proguard/gui/ListPanel.java
@@ -0,0 +1,317 @@
+/* $Id: ListPanel.java,v 1.8 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.List;
+import java.util.*;
+
+import javax.swing.*;
+import javax.swing.event.*;
+
+/**
+ * This <code>Jpanel</code> allows the user to move and remove entries in a
+ * list and between lists. Extensions of this class should add buttons to add
+ * and possibly edit entries, and to set and get the resulting list.
+ *
+ * @author Eric Lafortune
+ */
+abstract class ListPanel extends JPanel
+{
+    protected DefaultListModel listModel = new DefaultListModel();
+    protected JList            list      = new JList(listModel);
+
+    protected int firstSelectionButton = 2;
+
+
+    protected ListPanel()
+    {
+        GridBagLayout layout = new GridBagLayout();
+        setLayout(layout);
+
+        GridBagConstraints listConstraints = new GridBagConstraints();
+        listConstraints.gridheight = GridBagConstraints.REMAINDER;
+        listConstraints.fill       = GridBagConstraints.BOTH;
+        listConstraints.weightx    = 1.0;
+        listConstraints.weighty    = 1.0;
+        listConstraints.anchor     = GridBagConstraints.NORTHWEST;
+        listConstraints.insets     = new Insets(0, 2, 0, 2);
+
+        // Make sure some buttons are disabled or enabled depending on whether
+        // the selection is empty or not.
+        list.addListSelectionListener(new ListSelectionListener()
+        {
+            public void valueChanged(ListSelectionEvent e)
+            {
+                enableSelectionButtons();
+            }
+        });
+
+        add(new JScrollPane(list), listConstraints);
+
+        // something like the following calls are up to the extending class:
+        //addAddButton();
+        //addEditButton();
+        //addRemoveButton();
+        //addUpButton();
+        //addDownButton();
+        //
+        //enableSelectionButtons();
+    }
+
+
+    protected void addRemoveButton()
+    {
+        JButton removeButton = new JButton(GUIResources.getMessage("remove"));
+        removeButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                // Remove the selected elements.
+                removeElementsAt(list.getSelectedIndices());
+            }
+        });
+
+        addButton(removeButton);
+    }
+
+
+    protected void addUpButton()
+    {
+        JButton upButton = new JButton(GUIResources.getMessage("moveUp"));
+        upButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                int[] selectedIndices = list.getSelectedIndices();
+                if (selectedIndices.length > 0 &&
+                    selectedIndices[0] > 0)
+                {
+                    // Move the selected elements up.
+                    moveElementsAt(selectedIndices, -1);
+                }
+            }
+        });
+
+        addButton(upButton);
+    }
+
+
+    protected void addDownButton()
+    {
+        JButton downButton = new JButton(GUIResources.getMessage("moveDown"));
+        downButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                int[] selectedIndices = list.getSelectedIndices();
+                if (selectedIndices.length > 0 &&
+                    selectedIndices[selectedIndices.length-1] < listModel.getSize()-1)
+                {
+                    // Move the selected elements down.
+                    moveElementsAt(selectedIndices, 1);
+                }
+            }
+        });
+
+        addButton(downButton);
+    }
+
+
+    /**
+     * Adds a button that allows to copy or move entries to another ListPanel.
+     *
+     * @param buttonText the button text.
+     * @param panel      the other ListPanel.
+     */
+    public void addCopyToPanelButton(String          buttonText,
+                                     final ListPanel panel)
+    {
+        JButton moveButton = new JButton(buttonText);
+        moveButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                int[]    selectedIndices  = list.getSelectedIndices();
+                Object[] selectedElements = list.getSelectedValues();
+
+                // Remove the selected elements from this panel.
+                removeElementsAt(selectedIndices);
+
+                // Add the elements to the other panel.
+                panel.addElements(selectedElements);
+            }
+        });
+
+        addButton(moveButton);
+    }
+
+
+    protected void addButton(JButton button)
+    {
+        GridBagConstraints buttonConstraints = new GridBagConstraints();
+        buttonConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        buttonConstraints.fill      = GridBagConstraints.HORIZONTAL;
+        buttonConstraints.anchor    = GridBagConstraints.NORTHWEST;
+        buttonConstraints.insets    = new Insets(0, 2, 0, 2);
+
+        add(button, buttonConstraints);
+    }
+
+
+    /**
+     * Returns a list of all right-hand side buttons.
+     */
+    public List getButtons()
+    {
+        List list = new ArrayList(getComponentCount()-1);
+
+        // Add all buttons.
+        for (int index = 1; index < getComponentCount(); index++)
+        {
+            list.add(getComponent(index));
+        }
+
+        return list;
+    }
+
+
+    protected void addElement(Object element)
+    {
+        listModel.addElement(element);
+
+        // Make sure it is selected.
+        list.setSelectedIndex(listModel.size() - 1);
+    }
+
+
+    protected void addElements(Object[] elements)
+    {
+        // Add the elements one by one.
+        for (int index = 0; index < elements.length; index++)
+        {
+            listModel.addElement(elements[index]);
+        }
+
+        // Make sure they are selected.
+        int[] selectedIndices = new int[elements.length];
+        for (int index = 0; index < selectedIndices.length; index++)
+        {
+            selectedIndices[index] =
+                listModel.size() - selectedIndices.length + index;
+        }
+        list.setSelectedIndices(selectedIndices);
+    }
+
+
+    protected void moveElementsAt(int[] indices, int offset)
+    {
+        // Remember the selected elements.
+        Object[] selectedElements = list.getSelectedValues();
+
+        // Remove the selected elements.
+        removeElementsAt(indices);
+
+        // Update the element indices.
+        for (int index = 0; index < indices.length; index++)
+        {
+            indices[index] += offset;
+        }
+
+        // Reinsert the selected elements.
+        insertElementsAt(selectedElements, indices);
+    }
+
+
+    protected void insertElementsAt(Object[] elements, int[] indices)
+    {
+        for (int index = 0; index < elements.length; index++)
+        {
+            listModel.insertElementAt(elements[index], indices[index]);
+        }
+
+        // Make sure they are selected.
+        list.setSelectedIndices(indices);
+    }
+
+
+    protected void setElementAt(Object element, int index)
+    {
+        listModel.setElementAt(element, index);
+
+        // Make sure it is selected.
+        list.setSelectedIndex(index);
+    }
+
+
+    protected void setElementsAt(Object[] elements, int[] indices)
+    {
+        for (int index = 0; index < elements.length; index++)
+        {
+            listModel.setElementAt(elements[index], indices[index]);
+        }
+
+        // Make sure they are selected.
+        list.setSelectedIndices(indices);
+    }
+
+
+    protected void removeElementsAt(int[] indices)
+    {
+        for (int index = indices.length - 1; index >= 0; index--)
+        {
+            listModel.removeElementAt(indices[index]);
+        }
+
+        // Make sure nothing is selected.
+        list.clearSelection();
+
+        // Make sure the selection buttons are properly enabled,
+        // since the above method doesn't seem to notify the listener.
+        enableSelectionButtons();
+    }
+
+
+    protected void removeAllElements()
+    {
+        listModel.removeAllElements();
+
+        // Make sure the selection buttons are properly enabled,
+        // since the above method doesn't seem to notify the listener.
+        enableSelectionButtons();
+    }
+
+
+    /**
+     * Enables or disables the buttons that depend on a selection.
+     */
+    protected void enableSelectionButtons()
+    {
+        boolean selected = !list.isSelectionEmpty();
+
+        // Loop over all components, except the list itself and the Add button.
+        for (int index = firstSelectionButton; index < getComponentCount(); index++)
+        {
+            getComponent(index).setEnabled(selected);
+        }
+    }
+}
diff --git a/src/proguard/gui/MessageDialogRunnable.java b/src/proguard/gui/MessageDialogRunnable.java
new file mode 100644
index 0000000..c3b6d1c
--- /dev/null
+++ b/src/proguard/gui/MessageDialogRunnable.java
@@ -0,0 +1,83 @@
+/* $Id: MessageDialogRunnable.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui;
+
+import java.awt.*;
+
+import javax.swing.*;
+
+
+/**
+ * This <code>Runnable</code> can show a message dialog.
+ *
+ * @author Eric Lafortune
+ */
+class MessageDialogRunnable implements Runnable
+{
+    private Component parentComponent;
+    private Object    message;
+    private String    title;
+    private int       messageType;
+
+
+    /**
+     * Creates a new MessageDialogRunnable object.
+     * @see JOptionPane.showMessageDialog
+     */
+    public static void showMessageDialog(Component parentComponent,
+                                         Object    message,
+                                         String    title,
+                                         int       messageType)
+    {
+        SwingUtil.invokeAndWait(new MessageDialogRunnable(parentComponent,
+                                                          message,
+                                                          title,
+                                                          messageType));
+    }
+
+
+    /**
+     * Creates a new MessageDialogRunnable object.
+     * @see JOptionPane.showMessageDialog
+     */
+    public MessageDialogRunnable(Component parentComponent,
+                                 Object    message,
+                                 String    title,
+                                 int       messageType)
+    {
+        this.parentComponent = parentComponent;
+        this.message         = message;
+        this.title           = title;
+        this.messageType     = messageType;
+    }
+
+
+
+    // Implementation for Runnable.
+
+    public void run()
+    {
+        JOptionPane.showMessageDialog(parentComponent,
+                                      message,
+                                      title,
+                                      messageType);
+    }
+}
diff --git a/src/proguard/gui/ProGuardGUI.java b/src/proguard/gui/ProGuardGUI.java
new file mode 100644
index 0000000..d5d9512
--- /dev/null
+++ b/src/proguard/gui/ProGuardGUI.java
@@ -0,0 +1,1420 @@
+/* $Id: ProGuardGUI.java,v 1.27 2004/11/20 15:08:57 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui;
+
+import proguard.*;
+import proguard.optimize.NoSideEffectMethodMarker;
+import proguard.util.*;
+import proguard.classfile.util.*;
+import proguard.gui.splash.*;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.net.URL;
+import java.util.*;
+import java.util.List;
+
+
+/**
+ * GUI for configuring and executing ProGuard and ReTrace.
+ *
+ * @author Eric Lafortune
+ */
+public class ProGuardGUI extends JFrame
+{
+    private static final String NO_SPLASH_OPTION = "-nosplash";
+
+    private static final String TITLE_IMAGE_FILE          = "vtitle.gif";
+    private static final String BOILERPLATE_CONFIGURATION = "boilerplate.pro";
+    private static final String DEFAULT_CONFIGURATION     = "default.pro";
+
+    private static final String KEEP_ATTRIBUTE_DEFAULT        = "InnerClasses,SourceFile,LineNumberTable,Deprecated,Signature,*Annotation*,EnclosingMethod";
+    private static final String SOURCE_FILE_ATTRIBUTE_DEFAULT = "SourceFile";
+
+    private static final Border BORDER = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
+
+    static boolean systemOutRedirected;
+
+    private JFileChooser configurationChooser = new JFileChooser("");
+    private JFileChooser fileChooser          = new JFileChooser("");
+
+    private SplashPanel splashPanel;
+
+    private ClassPathPanel programPanel = new ClassPathPanel(this, true);
+    private ClassPathPanel libraryPanel = new ClassPathPanel(this, false);
+
+    private ClassSpecification[] boilerplateKeep;
+    private JCheckBox[]          boilerplateKeepCheckBoxes;
+    private JTextField[]         boilerplateKeepTextFields;
+
+    private ClassSpecificationsPanel additionalKeepPanel = new ClassSpecificationsPanel(this, true);
+
+    private ClassSpecification[] boilerplateKeepNames;
+    private JCheckBox[]          boilerplateKeepNamesCheckBoxes;
+    private JTextField[]         boilerplateKeepNamesTextFields;
+
+    private ClassSpecificationsPanel additionalKeepNamesPanel = new ClassSpecificationsPanel(this, true);
+
+    private ClassSpecification[] boilerplateNoSideEffectMethods;
+    private JCheckBox[]          boilerplateNoSideEffectMethodCheckBoxes;
+
+    private ClassSpecificationsPanel additionalNoSideEffectsPanel = new ClassSpecificationsPanel(this, false);
+
+    private JCheckBox shrinkCheckBox                           = new JCheckBox(msg("shrink"));
+    private JCheckBox printUsageCheckBox                       = new JCheckBox(msg("printUsage"));
+
+    private JCheckBox optimizeCheckBox                         = new JCheckBox(msg("optimize"));
+    private JCheckBox allowAccessModificationCheckBox          = new JCheckBox(msg("allowAccessModification"));
+
+    private JCheckBox obfuscateCheckBox                        = new JCheckBox(msg("obfuscate"));
+    private JCheckBox printMappingCheckBox                     = new JCheckBox(msg("printMapping"));
+    private JCheckBox applyMappingCheckBox                     = new JCheckBox(msg("applyMapping"));
+    private JCheckBox obfuscationDictionaryCheckBox            = new JCheckBox(msg("obfuscationDictionary"));
+    private JCheckBox overloadAggressivelyCheckBox             = new JCheckBox(msg("overloadAggressively"));
+    private JCheckBox defaultPackageCheckBox                   = new JCheckBox(msg("defaultPackage"));
+    private JCheckBox useMixedCaseClassNamesCheckBox           = new JCheckBox(msg("useMixedCaseClassNames"));
+    private JCheckBox keepAttributesCheckBox                   = new JCheckBox(msg("keepAttributes"));
+    private JCheckBox newSourceFileAttributeCheckBox           = new JCheckBox(msg("renameSourceFileAttribute"));
+
+    private JCheckBox printSeedsCheckBox                       = new JCheckBox(msg("printSeeds"));
+    private JCheckBox verboseCheckBox                          = new JCheckBox(msg("verbose"));
+    private JCheckBox ignoreWarningsCheckBox                   = new JCheckBox(msg("ignoreWarnings"));
+    private JCheckBox warnCheckBox                             = new JCheckBox(msg("warn"));
+    private JCheckBox noteCheckBox                             = new JCheckBox(msg("note"));
+    private JCheckBox skipNonPublicLibraryClassesCheckBox      = new JCheckBox(msg("skipNonPublicLibraryClasses"));
+    private JCheckBox skipNonPublicLibraryClassMembersCheckBox = new JCheckBox(msg("skipNonPublicLibraryClassMembers"));
+
+    private JTextField printUsageTextField                     = new JTextField(40);
+    private JTextField printMappingTextField                   = new JTextField(40);
+    private JTextField applyMappingTextField                   = new JTextField(40);
+    private JTextField obfuscationDictionaryTextField          = new JTextField(40);
+    private JTextField defaultPackageTextField                 = new JTextField(40);
+    private JTextField keepAttributesTextField                 = new JTextField(40);
+    private JTextField newSourceFileAttributeTextField         = new JTextField(40);
+    private JTextField printSeedsTextField                     = new JTextField(40);
+
+    private JTextArea  consoleTextArea                         = new JTextArea(msg("processingInfo"), 3, 40);
+
+    private JTextField reTraceMappingTextField                 = new JTextField(40);
+    private JCheckBox  reTraceVerboseCheckBox                  = new JCheckBox(msg("verbose"));
+    private JTextArea  stackTraceTextArea                      = new JTextArea(3, 40);
+    private JTextArea  reTraceTextArea                         = new JTextArea(msg("reTraceInfo"), 3, 40);
+
+
+    /**
+     * Creates a new ProGuardGUI.
+     */
+    public ProGuardGUI()
+    {
+        setTitle("ProGuard");
+        setDefaultCloseOperation(EXIT_ON_CLOSE);
+
+        // Create some constraints that can be reused.
+        GridBagConstraints constraints = new GridBagConstraints();
+        constraints.anchor = GridBagConstraints.WEST;
+        constraints.insets = new Insets(0, 4, 0, 4);
+
+        GridBagConstraints constraintsStretch = new GridBagConstraints();
+        constraintsStretch.fill    = GridBagConstraints.HORIZONTAL;
+        constraintsStretch.weightx = 1.0;
+        constraintsStretch.anchor  = GridBagConstraints.WEST;
+        constraintsStretch.insets  = constraints.insets;
+
+        GridBagConstraints constraintsLast = new GridBagConstraints();
+        constraintsLast.gridwidth = GridBagConstraints.REMAINDER;
+        constraintsLast.anchor    = GridBagConstraints.WEST;
+        constraintsLast.insets    = constraints.insets;
+
+        GridBagConstraints constraintsLastStretch = new GridBagConstraints();
+        constraintsLastStretch.gridwidth = GridBagConstraints.REMAINDER;
+        constraintsLastStretch.fill      = GridBagConstraints.HORIZONTAL;
+        constraintsLastStretch.weightx   = 1.0;
+        constraintsLastStretch.anchor    = GridBagConstraints.WEST;
+        constraintsLastStretch.insets    = constraints.insets;
+
+        GridBagConstraints splashPanelConstraints = new GridBagConstraints();
+        splashPanelConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        splashPanelConstraints.fill      = GridBagConstraints.BOTH;
+        splashPanelConstraints.weightx   = 1.0;
+        splashPanelConstraints.weighty   = 0.02;
+        splashPanelConstraints.anchor    = GridBagConstraints.NORTHWEST;
+        //splashPanelConstraints.insets    = constraints.insets;
+
+        GridBagConstraints welcomeTextAreaConstraints = new GridBagConstraints();
+        welcomeTextAreaConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        welcomeTextAreaConstraints.fill      = GridBagConstraints.NONE;
+        welcomeTextAreaConstraints.weightx   = 1.0;
+        welcomeTextAreaConstraints.weighty   = 0.01;
+        welcomeTextAreaConstraints.anchor    = GridBagConstraints.CENTER;//NORTHWEST;
+        welcomeTextAreaConstraints.insets    = new Insets(20, 40, 20, 40);
+
+        GridBagConstraints panelConstraints = new GridBagConstraints();
+        panelConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        panelConstraints.fill      = GridBagConstraints.HORIZONTAL;
+        panelConstraints.weightx   = 1.0;
+        panelConstraints.anchor    = GridBagConstraints.NORTHWEST;
+        panelConstraints.insets    = constraints.insets;
+
+        GridBagConstraints stretchPanelConstraints = new GridBagConstraints();
+        stretchPanelConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        stretchPanelConstraints.fill      = GridBagConstraints.BOTH;
+        stretchPanelConstraints.weightx   = 1.0;
+        stretchPanelConstraints.weighty   = 1.0;
+        stretchPanelConstraints.anchor    = GridBagConstraints.NORTHWEST;
+        stretchPanelConstraints.insets    = constraints.insets;
+
+        GridBagConstraints glueConstraints = new GridBagConstraints();
+        glueConstraints.fill    = GridBagConstraints.BOTH;
+        glueConstraints.weightx = 0.01;
+        glueConstraints.weighty = 0.01;
+        glueConstraints.anchor  = GridBagConstraints.NORTHWEST;
+        glueConstraints.insets  = constraints.insets;
+
+        GridBagConstraints bottomButtonConstraints = new GridBagConstraints();
+        bottomButtonConstraints.anchor = GridBagConstraints.SOUTHEAST;
+        bottomButtonConstraints.insets = new Insets(2, 2, 4, 6);
+        bottomButtonConstraints.ipadx  = 10;
+        bottomButtonConstraints.ipady  = 2;
+
+        GridBagConstraints lastBottomButtonConstraints = new GridBagConstraints();
+        lastBottomButtonConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        lastBottomButtonConstraints.anchor    = GridBagConstraints.SOUTHEAST;
+        lastBottomButtonConstraints.insets    = bottomButtonConstraints.insets;
+        lastBottomButtonConstraints.ipadx     = bottomButtonConstraints.ipadx;
+        lastBottomButtonConstraints.ipady     = bottomButtonConstraints.ipady;
+
+        GridBagLayout layout = new GridBagLayout();
+
+        configurationChooser.addChoosableFileFilter(
+            new ExtensionFileFilter(msg("proExtension"), new String[] { ".pro" }));
+
+        // Create the opening panel.
+        //JLabel titleLabel = new JLabel("ProGuard", JLabel.CENTER);
+        //titleLabel.setFont(new Font("serif", Font.BOLD, 40));
+        //titleLabel.setForeground(Color.gray);
+
+        Font font = new Font("sansserif", Font.BOLD, 50);
+        Color fontColor = Color.white;
+
+        Sprite splash =
+            new CompositeSprite(new Sprite[]
+        {
+            new TextSprite(new ConstantString("ProGuard"),
+                           new ConstantFont(new Font("sansserif", Font.BOLD, 90)),
+                           new ConstantColor(Color.gray),
+                           new ConstantInt(160),
+                           new LinearInt(-10, 120, new SmoothTiming(500, 1000))),
+
+            new ShadowedSprite(new ConstantInt(3),
+                               new ConstantInt(3),
+                               new ConstantDouble(0.4),
+                               new ConstantInt(2),
+                               new CompositeSprite(new Sprite[]
+            {
+                new TextSprite(new ConstantString(msg("shrinking")),
+                               new ConstantFont(font),
+                               new ConstantColor(fontColor),
+                               new LinearInt(1000, 60, new SmoothTiming(1000, 2000)),
+                               new ConstantInt(70)),
+                new TextSprite(new ConstantString(msg("optimization")),
+                               new ConstantFont(font),
+                               new ConstantColor(fontColor),
+                               new LinearInt(1000, 400, new SmoothTiming(1500, 2500)),
+                               new ConstantInt(60)),
+                new TextSprite(new ConstantString(msg("obfuscation")),
+                               new ConstantFont(font),
+                               new ConstantColor(fontColor),
+                               new LinearInt(1000, 350, new SmoothTiming(2000, 3000)),
+                               new ConstantInt(140)),
+                new TextSprite(new TypeWriterString(msg("developed"), new LinearTiming(3000, 5000)),
+                               new ConstantFont(new Font("monospaced", Font.BOLD, 20)),
+                               new ConstantColor(fontColor),
+                               new ConstantInt(250),
+                               new ConstantInt(170)),
+            })),
+        });
+        splashPanel = new SplashPanel(splash, 0.5, 5000L);
+        splashPanel.setPreferredSize(new Dimension(0, 200));
+
+        JTextArea welcomeTextArea = new JTextArea(msg("proGuardInfo"), 18, 50);
+        welcomeTextArea.setOpaque(false);
+        welcomeTextArea.setEditable(false);
+        welcomeTextArea.setLineWrap(true);
+        welcomeTextArea.setWrapStyleWord(true);
+        welcomeTextArea.setPreferredSize(new Dimension(0, 0));
+        welcomeTextArea.setBorder(new EmptyBorder(20, 20, 20, 20));
+        addBorder(welcomeTextArea, "welcome");
+
+        JPanel proGuardPanel = new JPanel(layout);
+        proGuardPanel.add(splashPanel,      splashPanelConstraints);
+        proGuardPanel.add(welcomeTextArea,  welcomeTextAreaConstraints);
+
+        // Create the input panel.
+        // TODO: properly clone the ClassPath objects.
+        // This is awkward to implement in the generic ListPanel.addElements(...)
+        // method, since the Object.clone() method is not public.
+        programPanel.addCopyToPanelButton(msg("moveToLibraries"), libraryPanel);
+        libraryPanel.addCopyToPanelButton(msg("moveToProgram"),   programPanel);
+
+        // Collect all buttons of these panels and make sure they are equally
+        // sized.
+        List panelButtons = new ArrayList();
+        panelButtons.addAll(programPanel.getButtons());
+        panelButtons.addAll(libraryPanel.getButtons());
+        setCommonPreferredSize(panelButtons);
+        panelButtons = null;
+
+        addBorder(programPanel, "programJars" );
+        addBorder(libraryPanel, "libraryJars" );
+
+        JPanel inputOutputPanel = new JPanel(layout);
+        inputOutputPanel.add(programPanel, stretchPanelConstraints);
+        inputOutputPanel.add(libraryPanel, stretchPanelConstraints);
+
+        // Load the boiler plate options.
+        loadBoilerplateConfiguration();
+
+        // Create the boiler plate keep panels.
+        boilerplateKeepCheckBoxes = new JCheckBox[boilerplateKeep.length];
+        boilerplateKeepTextFields = new JTextField[boilerplateKeep.length];
+
+        JButton printUsageBrowseButton   = createBrowseButton(printUsageTextField,
+                                                              msg("selectUsageFile"));
+
+        JPanel shrinkingOptionsPanel = new JPanel(layout);
+        addBorder(shrinkingOptionsPanel, "options");
+
+        shrinkingOptionsPanel.add(shrinkCheckBox,         constraintsLastStretch);
+        shrinkingOptionsPanel.add(printUsageCheckBox,     constraints);
+        shrinkingOptionsPanel.add(printUsageTextField,    constraintsStretch);
+        shrinkingOptionsPanel.add(printUsageBrowseButton, constraintsLast);
+
+        JPanel shrinkingPanel = new JPanel(layout);
+
+        shrinkingPanel.add(shrinkingOptionsPanel, panelConstraints);
+        addClassSpecifications(boilerplateKeep,
+                               shrinkingPanel,
+                               boilerplateKeepCheckBoxes,
+                               boilerplateKeepTextFields);
+
+        addBorder(additionalKeepPanel, "keepAdditional");
+        shrinkingPanel.add(additionalKeepPanel, stretchPanelConstraints);
+
+        // Create the boiler plate keep names panels.
+        boilerplateKeepNamesCheckBoxes = new JCheckBox[boilerplateKeepNames.length];
+        boilerplateKeepNamesTextFields = new JTextField[boilerplateKeepNames.length];
+
+        JButton printMappingBrowseButton = createBrowseButton(printMappingTextField,
+                                                              msg("selectPrintMappingFile"));
+        JButton applyMappingBrowseButton = createBrowseButton(applyMappingTextField,
+                                                              msg("selectApplyMappingFile"));
+        JButton obfucationDictionaryBrowseButton = createBrowseButton(obfuscationDictionaryTextField,
+                                                                      msg("selectObfuscationDictionaryFile"));
+
+        JPanel obfuscationOptionsPanel = new JPanel(layout);
+        addBorder(obfuscationOptionsPanel, "options");
+
+        obfuscationOptionsPanel.add(obfuscateCheckBox,                constraintsLastStretch);
+        obfuscationOptionsPanel.add(printMappingCheckBox,             constraints);
+        obfuscationOptionsPanel.add(printMappingTextField,            constraintsStretch);
+        obfuscationOptionsPanel.add(printMappingBrowseButton,         constraintsLast);
+        obfuscationOptionsPanel.add(applyMappingCheckBox,             constraints);
+        obfuscationOptionsPanel.add(applyMappingTextField,            constraintsStretch);
+        obfuscationOptionsPanel.add(applyMappingBrowseButton,         constraintsLast);
+        obfuscationOptionsPanel.add(obfuscationDictionaryCheckBox,    constraints);
+        obfuscationOptionsPanel.add(obfuscationDictionaryTextField,   constraintsStretch);
+        obfuscationOptionsPanel.add(obfucationDictionaryBrowseButton, constraintsLast);
+        obfuscationOptionsPanel.add(overloadAggressivelyCheckBox,     constraintsLastStretch);
+        obfuscationOptionsPanel.add(defaultPackageCheckBox,           constraints);
+        obfuscationOptionsPanel.add(defaultPackageTextField,          constraintsLastStretch);
+        obfuscationOptionsPanel.add(useMixedCaseClassNamesCheckBox,   constraintsLastStretch);
+        obfuscationOptionsPanel.add(keepAttributesCheckBox,           constraints);
+        obfuscationOptionsPanel.add(keepAttributesTextField,          constraintsLastStretch);
+        obfuscationOptionsPanel.add(newSourceFileAttributeCheckBox,   constraints);
+        obfuscationOptionsPanel.add(newSourceFileAttributeTextField,  constraintsLastStretch);
+
+        JPanel obfuscationPanel = new JPanel(layout);
+
+        obfuscationPanel.add(obfuscationOptionsPanel, panelConstraints);
+        addClassSpecifications(boilerplateKeepNames,
+                               obfuscationPanel,
+                               boilerplateKeepNamesCheckBoxes,
+                               boilerplateKeepNamesTextFields);
+
+        addBorder(additionalKeepNamesPanel, "keepNamesAdditional");
+        obfuscationPanel.add(additionalKeepNamesPanel, stretchPanelConstraints);
+
+        // Create the boiler plate "no side effect methods" panels.
+        boilerplateNoSideEffectMethodCheckBoxes = new JCheckBox[boilerplateNoSideEffectMethods.length];
+
+        JPanel optimizationOptionsPanel = new JPanel(layout);
+        addBorder(optimizationOptionsPanel, "options");
+
+        optimizationOptionsPanel.add(optimizeCheckBox,                constraintsLastStretch);
+        optimizationOptionsPanel.add(allowAccessModificationCheckBox, constraintsLastStretch);
+
+        JPanel optimizationPanel = new JPanel(layout);
+
+        optimizationPanel.add(optimizationOptionsPanel, panelConstraints);
+        addClassSpecifications(boilerplateNoSideEffectMethods,
+                               optimizationPanel,
+                               boilerplateNoSideEffectMethodCheckBoxes,
+                               null);
+
+        addBorder(additionalNoSideEffectsPanel, "assumeNoSideEffectsAdditional");
+        optimizationPanel.add(additionalNoSideEffectsPanel, stretchPanelConstraints);
+
+        // Create the options panel.
+        JButton printSeedsBrowseButton   = createBrowseButton(printSeedsTextField,
+                                                              msg("selectSeedsFile"));
+
+        JPanel consistencyPanel = new JPanel(layout);
+        addBorder(consistencyPanel, "consistencyAndCorrectness");
+
+        consistencyPanel.add(printSeedsCheckBox,                       constraints);
+        consistencyPanel.add(printSeedsTextField,                      constraintsStretch);
+        consistencyPanel.add(printSeedsBrowseButton,                   constraintsLast);
+        consistencyPanel.add(verboseCheckBox,                          constraintsLastStretch);
+        consistencyPanel.add(noteCheckBox,                             constraintsLastStretch);
+        consistencyPanel.add(warnCheckBox,                             constraintsLastStretch);
+        consistencyPanel.add(ignoreWarningsCheckBox,                   constraintsLastStretch);
+        consistencyPanel.add(skipNonPublicLibraryClassesCheckBox,      constraintsLastStretch);
+        consistencyPanel.add(skipNonPublicLibraryClassMembersCheckBox, constraintsLastStretch);
+
+        // Collect all components that are followed by text fields and make
+        // sure they are equally sized. That way the text fields start at the
+        // same horizontal position.
+        setCommonPreferredSize(Arrays.asList(new JComponent[] {
+            printMappingCheckBox,
+            applyMappingCheckBox,
+            defaultPackageCheckBox,
+            newSourceFileAttributeCheckBox,
+        }));
+
+        JPanel optionsPanel = new JPanel(layout);
+
+        optionsPanel.add(consistencyPanel,  panelConstraints);
+
+        // Create the process panel.
+        consoleTextArea.setOpaque(false);
+        consoleTextArea.setEditable(false);
+        consoleTextArea.setLineWrap(false);
+        consoleTextArea.setWrapStyleWord(false);
+        JScrollPane consoleScrollPane = new JScrollPane(consoleTextArea);
+        consoleScrollPane.setBorder(new EmptyBorder(1, 1, 1, 1));
+        addBorder(consoleScrollPane, "processingConsole");
+
+        JPanel processPanel = new JPanel(layout);
+        processPanel.add(consoleScrollPane, stretchPanelConstraints);
+
+        // Create the load, save, and process buttons.
+        JButton loadButton = new JButton(msg("loadConfiguration"));
+        loadButton.addActionListener(new MyLoadConfigurationActionListener());
+
+        JButton viewButton = new JButton(msg("viewConfiguration"));
+        viewButton.addActionListener(new MyViewConfigurationActionListener());
+
+        JButton saveButton = new JButton(msg("saveConfiguration"));
+        saveButton.addActionListener(new MySaveConfigurationActionListener());
+
+        JButton processButton = new JButton(msg("process"));
+        processButton.addActionListener(new MyProcessActionListener());
+
+        // Create the ReTrace panel.
+        JPanel reTraceSettingsPanel = new JPanel(layout);
+        addBorder(reTraceSettingsPanel, "reTraceSettings");
+
+        JButton reTraceMappingBrowseButton = createBrowseButton(reTraceMappingTextField,
+                                                                msg("selectApplyMappingFile"));
+
+        JLabel reTraceMappingLabel = new JLabel(msg("mappingFile"));
+        reTraceMappingLabel.setForeground(reTraceVerboseCheckBox.getForeground());
+
+        reTraceSettingsPanel.add(reTraceMappingLabel,        constraints);
+        reTraceSettingsPanel.add(reTraceMappingTextField,    constraintsStretch);
+        reTraceSettingsPanel.add(reTraceMappingBrowseButton, constraintsLast);
+        reTraceSettingsPanel.add(reTraceVerboseCheckBox,     constraintsLastStretch);
+
+        stackTraceTextArea.setOpaque(true);
+        stackTraceTextArea.setEditable(true);
+        stackTraceTextArea.setLineWrap(false);
+        stackTraceTextArea.setWrapStyleWord(true);
+        JScrollPane stackTraceScrollPane = new JScrollPane(stackTraceTextArea);
+        addBorder(stackTraceScrollPane, "obfuscatedStackTrace");
+
+        reTraceTextArea.setOpaque(false);
+        reTraceTextArea.setEditable(false);
+        reTraceTextArea.setLineWrap(true);
+        reTraceTextArea.setWrapStyleWord(true);
+        JScrollPane reTraceScrollPane = new JScrollPane(reTraceTextArea);
+        reTraceScrollPane.setBorder(new EmptyBorder(1, 1, 1, 1));
+        addBorder(reTraceScrollPane, "deobfuscatedStackTrace");
+
+        JPanel reTracePanel = new JPanel(layout);
+        reTracePanel.add(reTraceSettingsPanel, panelConstraints);
+        reTracePanel.add(stackTraceScrollPane, panelConstraints);
+        reTracePanel.add(reTraceScrollPane,    stretchPanelConstraints);
+
+        // Create the load button.
+        JButton loadStackTraceButton = new JButton(msg("loadStackTrace"));
+        loadStackTraceButton.addActionListener(new MyLoadStackTraceActionListener());
+
+        JButton reTraceButton = new JButton(msg("reTrace"));
+        reTraceButton.addActionListener(new MyReTraceActionListener());
+
+        // Create the main tabbed pane.
+        TabbedPane tabs = new TabbedPane();
+        tabs.add(msg("proGuardTab"),     proGuardPanel);
+        tabs.add(msg("inputOutputTab"),  inputOutputPanel);
+        tabs.add(msg("shrinkingTab"),    shrinkingPanel);
+        tabs.add(msg("obfuscationTab"),  obfuscationPanel);
+        tabs.add(msg("optimizationTab"), optimizationPanel);
+        tabs.add(msg("informationTab"),  optionsPanel);
+        tabs.add(msg("processTab"),      processPanel);
+        tabs.add(msg("reTraceTab"),      reTracePanel);
+        tabs.addImage(Toolkit.getDefaultToolkit().getImage(
+            this.getClass().getResource(TITLE_IMAGE_FILE)));
+
+        // Add the bottom buttons to each panel.
+        proGuardPanel     .add(Box.createGlue(),           glueConstraints);
+        proGuardPanel     .add(loadButton,                 bottomButtonConstraints);
+        proGuardPanel     .add(createNextButton(tabs),     lastBottomButtonConstraints);
+
+        inputOutputPanel  .add(Box.createGlue(),           glueConstraints);
+        inputOutputPanel  .add(createPreviousButton(tabs), bottomButtonConstraints);
+        inputOutputPanel  .add(createNextButton(tabs),     lastBottomButtonConstraints);
+
+        shrinkingPanel    .add(Box.createGlue(),           glueConstraints);
+        shrinkingPanel    .add(createPreviousButton(tabs), bottomButtonConstraints);
+        shrinkingPanel    .add(createNextButton(tabs),     lastBottomButtonConstraints);
+
+        obfuscationPanel  .add(Box.createGlue(),           glueConstraints);
+        obfuscationPanel  .add(createPreviousButton(tabs), bottomButtonConstraints);
+        obfuscationPanel  .add(createNextButton(tabs),     lastBottomButtonConstraints);
+
+        optimizationPanel .add(Box.createGlue(),           glueConstraints);
+        optimizationPanel .add(createPreviousButton(tabs), bottomButtonConstraints);
+        optimizationPanel .add(createNextButton(tabs),     lastBottomButtonConstraints);
+
+        optionsPanel      .add(Box.createGlue(),           glueConstraints);
+        optionsPanel      .add(createPreviousButton(tabs), bottomButtonConstraints);
+        optionsPanel      .add(createNextButton(tabs),     lastBottomButtonConstraints);
+
+        processPanel      .add(Box.createGlue(),           glueConstraints);
+        processPanel      .add(createPreviousButton(tabs), bottomButtonConstraints);
+        processPanel      .add(viewButton,                 bottomButtonConstraints);
+        processPanel      .add(saveButton,                 bottomButtonConstraints);
+        processPanel      .add(processButton,              lastBottomButtonConstraints);
+
+        reTracePanel      .add(Box.createGlue(),           glueConstraints);
+        reTracePanel      .add(loadStackTraceButton,       bottomButtonConstraints);
+        reTracePanel      .add(reTraceButton,              lastBottomButtonConstraints);
+
+        // Initialize the GUI settings to reasonable defaults.
+        loadConfiguration(this.getClass().getResource(DEFAULT_CONFIGURATION));
+
+        // Add the main tabs to the frame and pack it.
+        getContentPane().add(tabs);
+    }
+
+
+    public void startSplash()
+    {
+        splashPanel.start();
+    }
+
+
+    public void skipSplash()
+    {
+        splashPanel.stop();
+    }
+
+
+    /**
+     * Loads the boilerplate keep class file options from the boilerplate file
+     * into the boilerplate array.
+     */
+    private void loadBoilerplateConfiguration()
+    {
+        try
+        {
+            // Parse the boilerplate configuration file.
+            ConfigurationParser parser = new ConfigurationParser(
+                this.getClass().getResource(BOILERPLATE_CONFIGURATION));
+            Configuration configuration = new Configuration();
+            parser.parse(configuration);
+
+            // We're interested in the keep options.
+            boilerplateKeep = new ClassSpecification[configuration.keep.size()];
+            configuration.keep.toArray(boilerplateKeep);
+
+            // We're interested in the keep names options.
+            boilerplateKeepNames = new ClassSpecification[configuration.keepNames.size()];
+            configuration.keepNames.toArray(boilerplateKeepNames);
+
+            // We're interested in the side effects options.
+            boilerplateNoSideEffectMethods = new ClassSpecification[configuration.assumeNoSideEffects.size()];
+            configuration.assumeNoSideEffects.toArray(boilerplateNoSideEffectMethods);
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+
+
+    /**
+     * Creates a panel with the given boiler plate class specifications.
+     */
+    private void addClassSpecifications(ClassSpecification[] boilerplateClassSpecifications,
+                                        JPanel               classSpecificationsPanel,
+                                        JCheckBox[]          boilerplateCheckBoxes,
+                                        JTextField[]         boilerplateTextFields)
+    {
+        // Create some constraints that can be reused.
+        GridBagConstraints constraints = new GridBagConstraints();
+        constraints.anchor = GridBagConstraints.WEST;
+        constraints.insets = new Insets(0, 4, 0, 4);
+
+        GridBagConstraints constraintsLastStretch = new GridBagConstraints();
+        constraintsLastStretch.gridwidth = GridBagConstraints.REMAINDER;
+        constraintsLastStretch.fill      = GridBagConstraints.HORIZONTAL;
+        constraintsLastStretch.weightx   = 1.0;
+        constraintsLastStretch.anchor    = GridBagConstraints.WEST;
+        constraintsLastStretch.insets    = constraints.insets;
+
+        GridBagConstraints panelConstraints = new GridBagConstraints();
+        panelConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        panelConstraints.fill      = GridBagConstraints.HORIZONTAL;
+        panelConstraints.weightx   = 1.0;
+        panelConstraints.anchor    = GridBagConstraints.NORTHWEST;
+        panelConstraints.insets    = constraints.insets;
+
+        GridBagLayout layout = new GridBagLayout();
+
+        String lastPanelName = null;
+        JPanel keepSubpanel  = null;
+        for (int index = 0; index < boilerplateClassSpecifications.length; index++)
+        {
+            ClassSpecification classSpecification =
+                boilerplateClassSpecifications[index];
+
+            // The panel structure is derived from the comments.
+            String comments = classSpecification.comments;
+            int dashIndex = comments.indexOf('-');
+            int periodIndex = comments.indexOf('.', dashIndex);
+            String panelName = comments.substring(0, dashIndex).trim();
+            String optionName = comments.substring(dashIndex + 1, periodIndex).trim();
+            if (!panelName.equals(lastPanelName))
+            {
+                // Create a new keep subpanel and add it.
+                keepSubpanel = new JPanel(layout);
+                String titleKey = "boilerplate_" + panelName.toLowerCase().replace(' ', '_');
+                addBorder(keepSubpanel, titleKey);
+                classSpecificationsPanel.add(keepSubpanel, panelConstraints);
+
+                lastPanelName = panelName;
+            }
+
+            // Add the check box to the subpanel.
+            String messageKey = "boilerplate_" + optionName.toLowerCase().replace(' ', '_');
+            boilerplateCheckBoxes[index] = new JCheckBox(msg(messageKey));
+            keepSubpanel.add(boilerplateCheckBoxes[index],
+                             boilerplateTextFields != null ?
+                                 constraints :
+                                 constraintsLastStretch);
+
+            if (boilerplateTextFields != null)
+            {
+                // Add the text field to the subpanel.
+                boilerplateTextFields[index] = new JTextField(40);
+                keepSubpanel.add(boilerplateTextFields[index], constraintsLastStretch);
+            }
+        }
+    }
+
+
+    /**
+     * Adds a standard border with the title that corresponds to the given key
+     * in the GUI resources.
+     */
+    private void addBorder(JComponent component, String titleKey)
+    {
+        Border oldBorder = component.getBorder();
+        Border newBorder = BorderFactory.createTitledBorder(BORDER, msg(titleKey));
+
+        component.setBorder(oldBorder == null ?
+            newBorder :
+            new CompoundBorder(newBorder, oldBorder));
+    }
+
+
+    /**
+     * Creates a Previous button for the given tabbed pane.
+     */
+    private JButton createPreviousButton(final TabbedPane tabbedPane)
+    {
+        JButton browseButton = new JButton(msg("previous"));
+        browseButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                tabbedPane.previous();
+            }
+        });
+
+        return browseButton;
+    }
+
+
+    /**
+     * Creates a Next button for the given tabbed pane.
+     */
+    private JButton createNextButton(final TabbedPane tabbedPane)
+    {
+        JButton browseButton = new JButton(msg("next"));
+        browseButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                tabbedPane.next();
+            }
+        });
+
+        return browseButton;
+    }
+
+
+    /**
+     * Creates a browse button that opens a file browser for the given text field.
+     */
+    private JButton createBrowseButton(final JTextField textField,
+                                       final String     title)
+    {
+        JButton browseButton = new JButton(msg("browse"));
+        browseButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                fileChooser.setDialogTitle(title);
+                fileChooser.setSelectedFile(new File(textField.getText()));
+
+                int returnVal = fileChooser.showDialog(ProGuardGUI.this, msg("ok"));
+                if (returnVal == JFileChooser.APPROVE_OPTION)
+                {
+                    textField.setText(fileChooser.getSelectedFile().getPath());
+                }
+            }
+        });
+
+        return browseButton;
+    }
+
+
+    /**
+     * Sets the preferred sizes of the given components to the maximum of their
+     * current preferred sizes.
+     */
+    private void setCommonPreferredSize(List components)
+    {
+        // Find the maximum preferred size.
+        Dimension maximumSize = null;
+        for (int index = 0; index < components.size(); index++)
+        {
+            JComponent component = (JComponent)components.get(index);
+            Dimension  size      = component.getPreferredSize();
+            if (maximumSize == null ||
+                size.getWidth() > maximumSize.getWidth())
+            {
+                maximumSize = size;
+            }
+        }
+
+        // Set the size that we found as the preferred size for all components.
+        for (int index = 0; index < components.size(); index++)
+        {
+            JComponent component = (JComponent)components.get(index);
+            component.setPreferredSize(maximumSize);
+        }
+    }
+
+
+    /**
+     * Updates to GUI settings to reflect the given ProGuard configuration.
+     */
+    private void setProGuardConfiguration(Configuration configuration)
+    {
+        // Set up the input and output jars and directories.
+        programPanel.setClassPath(configuration.programJars);
+        libraryPanel.setClassPath(configuration.libraryJars);
+
+        // Set up the boilerplate keep options.
+        for (int index = 0; index < boilerplateKeep.length; index++)
+        {
+            String classNames =
+                findMatchingClassSpecifications(boilerplateKeep[index],
+                                                configuration.keep);
+
+            boilerplateKeepCheckBoxes[index].setSelected(classNames != null);
+            boilerplateKeepTextFields[index].setText(classNames == null ? "*" : classNames);
+        }
+
+        // Set up the additional keep options. Note that the matched boilerplate
+        // options have been removed from the list.
+        additionalKeepPanel.setClassSpecifications(configuration.keep);
+
+
+        // Set up the boilerplate keep names options.
+        for (int index = 0; index < boilerplateKeepNames.length; index++)
+        {
+            String classNames =
+                findMatchingClassSpecifications(boilerplateKeepNames[index],
+                                                configuration.keepNames);
+
+            boilerplateKeepNamesCheckBoxes[index].setSelected(classNames != null);
+            boilerplateKeepNamesTextFields[index].setText(classNames == null ? "*" : classNames);
+        }
+
+        // Set up the additional keep options. Note that the matched boilerplate
+        // options have been removed from the list.
+        additionalKeepNamesPanel.setClassSpecifications(configuration.keepNames);
+
+
+        // Set up the boilerplate "no side effect methods" options.
+        for (int index = 0; index < boilerplateNoSideEffectMethods.length; index++)
+        {
+            boolean found =
+                findClassSpecification(boilerplateNoSideEffectMethods[index],
+                                       configuration.assumeNoSideEffects);
+
+            boilerplateNoSideEffectMethodCheckBoxes[index].setSelected(found);
+        }
+
+        // Set up the additional keep options. Note that the matched boilerplate
+        // options have been removed from the list.
+        additionalNoSideEffectsPanel.setClassSpecifications(configuration.assumeNoSideEffects);
+
+        // Set up the other options.
+        shrinkCheckBox                          .setSelected(configuration.shrink);
+        printUsageCheckBox                      .setSelected(configuration.printUsage != null);
+
+        optimizeCheckBox                        .setSelected(configuration.optimize);
+        allowAccessModificationCheckBox         .setSelected(configuration.allowAccessModification);
+
+        obfuscateCheckBox                       .setSelected(configuration.obfuscate);
+        printMappingCheckBox                    .setSelected(configuration.printMapping != null);
+        applyMappingCheckBox                    .setSelected(configuration.applyMapping != null);
+        obfuscationDictionaryCheckBox           .setSelected(configuration.defaultPackage != null);
+        overloadAggressivelyCheckBox            .setSelected(configuration.overloadAggressively);
+        defaultPackageCheckBox                  .setSelected(configuration.obfuscationDictionary != null);
+        useMixedCaseClassNamesCheckBox          .setSelected(configuration.useMixedCaseClassNames);
+        keepAttributesCheckBox                  .setSelected(configuration.keepAttributes != null);
+        newSourceFileAttributeCheckBox          .setSelected(configuration.newSourceFileAttribute != null);
+
+        printSeedsCheckBox                      .setSelected(configuration.printSeeds != null);
+        verboseCheckBox                         .setSelected(configuration.verbose);
+        noteCheckBox                            .setSelected(configuration.note);
+        warnCheckBox                            .setSelected(configuration.warn);
+        ignoreWarningsCheckBox                  .setSelected(configuration.ignoreWarnings);
+        skipNonPublicLibraryClassesCheckBox     .setSelected(configuration.skipNonPublicLibraryClasses);
+        skipNonPublicLibraryClassMembersCheckBox.setSelected(configuration.skipNonPublicLibraryClassMembers);
+
+        printUsageTextField                     .setText(configuration.printUsage);
+        printMappingTextField                   .setText(configuration.printMapping);
+        applyMappingTextField                   .setText(configuration.applyMapping);
+        obfuscationDictionaryTextField          .setText(configuration.obfuscationDictionary);
+        defaultPackageTextField                 .setText(configuration.defaultPackage);
+        keepAttributesTextField                 .setText(configuration.keepAttributes         == null ? KEEP_ATTRIBUTE_DEFAULT : ListUtil.commaSeparatedString(configuration.keepAttributes));
+        newSourceFileAttributeTextField         .setText(configuration.newSourceFileAttribute == null ? SOURCE_FILE_ATTRIBUTE_DEFAULT : configuration.newSourceFileAttribute);
+        printSeedsTextField                     .setText(configuration.printSeeds);
+
+        if (configuration.printMapping != null)
+        {
+            reTraceMappingTextField.setText(configuration.printMapping);
+        }
+    }
+
+
+    /**
+     * Returns the ProGuard configuration that reflects the current GUI settings.
+     */
+    private Configuration getProGuardConfiguration()
+    {
+        Configuration configuration = new Configuration();
+
+        // Get the input and output jars and directories.
+        configuration.programJars = programPanel.getClassPath();
+        configuration.libraryJars = libraryPanel.getClassPath();
+
+        // Collect the boilerplate keep options.
+        List keep = new ArrayList();
+
+        for (int index = 0; index < boilerplateKeep.length; index++)
+        {
+            if (boilerplateKeepCheckBoxes[index].isSelected())
+            {
+                addClassSpecifications(keep,
+                                       boilerplateKeep[index],
+                                       boilerplateKeepTextFields[index].getText());
+            }
+        }
+
+        // Collect the additional keep options.
+        List additionalKeep = additionalKeepPanel.getClassSpecifications();
+        if (additionalKeep != null)
+        {
+            keep.addAll(additionalKeep);
+        }
+
+        // Put the list of keep options in the configuration.
+        if (keep.size() > 0)
+        {
+            configuration.keep = keep;
+        }
+
+
+        // Collect the boilerplate keep names options.
+        List keepNames = new ArrayList();
+
+        for (int index = 0; index < boilerplateKeepNames.length; index++)
+        {
+            if (boilerplateKeepNamesCheckBoxes[index].isSelected())
+            {
+                addClassSpecifications(keepNames,
+                                       boilerplateKeepNames[index],
+                                       boilerplateKeepNamesTextFields[index].getText());
+            }
+        }
+
+        // Collect the additional keep names options.
+        List additionalKeepNames = additionalKeepNamesPanel.getClassSpecifications();
+        if (additionalKeepNames != null)
+        {
+            keepNames.addAll(additionalKeepNames);
+        }
+
+        // Put the list of keep names options in the configuration.
+        if (keepNames.size() > 0)
+        {
+            configuration.keepNames = keepNames;
+        }
+
+
+        // Collect the boilerplate "no side effect methods" options.
+        List noSideEffectMethods = new ArrayList();
+
+        for (int index = 0; index < boilerplateNoSideEffectMethods.length; index++)
+        {
+            if (boilerplateNoSideEffectMethodCheckBoxes[index].isSelected())
+            {
+                noSideEffectMethods.add(boilerplateNoSideEffectMethods[index]);
+            }
+        }
+
+        // Collect the additional "no side effect methods" options.
+        List additionalNoSideEffectOptions = additionalNoSideEffectsPanel.getClassSpecifications();
+        if (additionalNoSideEffectOptions != null)
+        {
+            noSideEffectMethods.addAll(additionalNoSideEffectOptions);
+        }
+
+        // Put the list of "no side effect methods" options in the configuration.
+        if (noSideEffectMethods.size() > 0)
+        {
+            configuration.assumeNoSideEffects = noSideEffectMethods;
+        }
+
+
+        // Get the other options.
+        configuration.shrink                           = shrinkCheckBox                          .isSelected();
+        configuration.printUsage                       = printUsageCheckBox                      .isSelected() ? printUsageTextField                                .getText() : null;
+
+        configuration.optimize                         = optimizeCheckBox                        .isSelected();
+        configuration.allowAccessModification          = allowAccessModificationCheckBox         .isSelected();
+
+        configuration.obfuscate                        = obfuscateCheckBox                       .isSelected();
+        configuration.printMapping                     = printMappingCheckBox                    .isSelected() ? printMappingTextField                              .getText() : null;
+        configuration.applyMapping                     = applyMappingCheckBox                    .isSelected() ? applyMappingTextField                              .getText() : null;
+        configuration.obfuscationDictionary            = obfuscationDictionaryCheckBox           .isSelected() ? obfuscationDictionaryTextField                     .getText()  : null;
+        configuration.overloadAggressively             = overloadAggressivelyCheckBox            .isSelected();
+        configuration.defaultPackage                   = defaultPackageCheckBox                  .isSelected() ? defaultPackageTextField                            .getText()  : null;
+        configuration.useMixedCaseClassNames           = useMixedCaseClassNamesCheckBox          .isSelected();
+        configuration.keepAttributes                   = keepAttributesCheckBox                  .isSelected() ? ListUtil.commaSeparatedList(keepAttributesTextField.getText()) : null;
+        configuration.newSourceFileAttribute           = newSourceFileAttributeCheckBox          .isSelected() ? newSourceFileAttributeTextField                    .getText()  : null;
+
+        configuration.printSeeds                       = printSeedsCheckBox                      .isSelected() ? printSeedsTextField                                .getText() : null;
+        configuration.verbose                          = verboseCheckBox                         .isSelected();
+        configuration.note                             = noteCheckBox                            .isSelected();
+        configuration.warn                             = warnCheckBox                            .isSelected();
+        configuration.ignoreWarnings                   = ignoreWarningsCheckBox                  .isSelected();
+        configuration.skipNonPublicLibraryClasses      = skipNonPublicLibraryClassesCheckBox     .isSelected();
+        configuration.skipNonPublicLibraryClassMembers = skipNonPublicLibraryClassMembersCheckBox.isSelected();
+
+        return configuration;
+    }
+
+
+    /**
+     * Looks in the given list for a ProGuard option that is identical to the
+     * given template. Returns true if it found, and removes the matching option
+     * as a side effect.
+     */
+    private boolean findClassSpecification(ClassSpecification classSpecificationTemplate,
+                                           List                classSpecifications)
+    {
+        if (classSpecifications == null)
+        {
+            return false;
+        }
+
+        for (int index = 0; index < classSpecifications.size(); index++)
+        {
+            if (classSpecificationTemplate.equals(classSpecifications.get(index)))
+            {
+                // Remove the matching option as a side effect.
+                classSpecifications.remove(index);
+
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Looks in the given list for ProGuard options that match the given template.
+     * Returns a comma-separated string of class file names from matching options,
+     * and removes the matching options as a side effect.
+     */
+    private String findMatchingClassSpecifications(ClassSpecification classSpecificationTemplate,
+                                                   List                classSpecifications)
+    {
+        if (classSpecifications == null)
+        {
+            return null;
+        }
+
+        StringBuffer buffer = null;
+
+        for (int index = 0; index < classSpecifications.size(); index++)
+        {
+            ClassSpecification listedClassSpecification =
+                (ClassSpecification)classSpecifications.get(index);
+            String className = listedClassSpecification.className;
+            classSpecificationTemplate.className = className;
+            if (classSpecificationTemplate.equals(listedClassSpecification))
+            {
+                if (buffer == null)
+                {
+                    buffer = new StringBuffer();
+                }
+                else
+                {
+                    buffer.append(',');
+                }
+                buffer.append(className == null ? "*" : ClassUtil.externalClassName(className));
+
+                // Remove the matching option as a side effect.
+                classSpecifications.remove(index--);
+            }
+        }
+
+        return buffer == null ? null : buffer.toString();
+    }
+
+
+    /**
+     * Adds ProGuard options to the given list, based on the given option
+     * template and the comma-separated list of class names to be filled in.
+     */
+    private void addClassSpecifications(List               classSpecifications,
+                                        ClassSpecification classSpecificationTemplate,
+                                        String             classNamesString)
+    {
+        List classNames = ListUtil.commaSeparatedList(classNamesString);
+
+        for (int index = 0; index < classNames.size(); index++)
+        {
+            String className = (String)classNames.get(index);
+
+            // Create a copy of the template.
+            ClassSpecification classSpecification =
+                (ClassSpecification)classSpecificationTemplate.clone();
+
+            // Set the class name in the copy.
+            classSpecification.className =
+                className.equals("") ||
+                className.equals("*") ?
+                    null :
+                    ClassUtil.internalClassName(className);
+
+            // Add the copy to the list.
+            classSpecifications.add(classSpecification);
+        }
+    }
+
+
+    // Methods and internal classes related to actions.
+
+    /**
+     * Loads the given ProGuard configuration into the GUI.
+     */
+    private void loadConfiguration(String fileName)
+    {
+        try
+        {
+            // Parse the configuration file.
+            ConfigurationParser parser = new ConfigurationParser(fileName);
+            Configuration configuration = new Configuration();
+            parser.parse(configuration);
+
+            // Let the GUI reflect the configuration.
+            setProGuardConfiguration(configuration);
+        }
+        catch (IOException ex)
+        {
+            JOptionPane.showMessageDialog(getContentPane(),
+                                          msg("cantOpenConfigurationFile", fileName),
+                                          msg("warning"),
+                                          JOptionPane.ERROR_MESSAGE);
+        }
+        catch (ParseException ex)
+        {
+            JOptionPane.showMessageDialog(getContentPane(),
+                                          msg("cantParseConfigurationFile", fileName),
+                                          msg("warning"),
+                                          JOptionPane.ERROR_MESSAGE);
+        }
+    }
+
+
+    /**
+     * Loads the given ProGuard configuration into the GUI.
+     */
+    private void loadConfiguration(URL url)
+    {
+        try
+        {
+            // Parse the configuration file.
+            ConfigurationParser parser = new ConfigurationParser(url);
+            Configuration configuration = new Configuration();
+            parser.parse(configuration);
+
+            // Let the GUI reflect the configuration.
+            setProGuardConfiguration(configuration);
+        }
+        catch (IOException ex)
+        {
+            JOptionPane.showMessageDialog(getContentPane(),
+                                          msg("cantOpenConfigurationFile", url),
+                                          msg("warning"),
+                                          JOptionPane.ERROR_MESSAGE);
+        }
+        catch (ParseException ex)
+        {
+            JOptionPane.showMessageDialog(getContentPane(),
+                                          msg("cantParseConfigurationFile", url),
+                                          msg("warning"),
+                                          JOptionPane.ERROR_MESSAGE);
+        }
+    }
+
+
+    /**
+     * Saves the current ProGuard configuration to the given file.
+     */
+    private void saveConfiguration(String fileName)
+    {
+        try
+        {
+            // Save the configuration file.
+            ConfigurationWriter writer = new ConfigurationWriter(fileName);
+            writer.write(getProGuardConfiguration());
+            writer.close();
+        }
+        catch (Exception ex)
+        {
+            JOptionPane.showMessageDialog(getContentPane(),
+                                          msg("cantSaveConfigurationFile", fileName),
+                                          msg("warning"),
+                                          JOptionPane.ERROR_MESSAGE);
+        }
+    }
+
+
+    /**
+     * Loads the given stack trace into the GUI.
+     */
+    private void loadStackTrace(String fileName)
+    {
+        try
+        {
+            // Read the entire stack trace file into a buffer.
+            File file = new File(fileName);
+            byte[] buffer = new byte[(int)file.length()];
+            InputStream inputStream = new FileInputStream(file);
+            inputStream.read(buffer);
+            inputStream.close();
+
+            // Put the stack trace in the text area.
+            stackTraceTextArea.setText(new String(buffer));
+        }
+        catch (IOException ex)
+        {
+            JOptionPane.showMessageDialog(getContentPane(),
+                                          msg("cantOpenStackTraceFile", fileName),
+                                          msg("warning"),
+                                          JOptionPane.ERROR_MESSAGE);
+        }
+    }
+
+
+    /**
+     * This ActionListener loads a ProGuard configuration file and initializes
+     * the GUI accordingly.
+     */
+    private class MyLoadConfigurationActionListener implements ActionListener
+    {
+        public void actionPerformed(ActionEvent e)
+        {
+            configurationChooser.setDialogTitle(msg("selectConfigurationFile"));
+
+            int returnValue = configurationChooser.showOpenDialog(ProGuardGUI.this);
+            if (returnValue == JFileChooser.APPROVE_OPTION)
+            {
+                File selectedFile = configurationChooser.getSelectedFile();
+                String fileName = selectedFile.getPath();
+
+                loadConfiguration(fileName);
+            }
+        }
+    }
+
+
+    /**
+     * This ActionListener saves a ProGuard configuration file based on the
+     * current GUI settings.
+     */
+    private class MySaveConfigurationActionListener implements ActionListener
+    {
+        public void actionPerformed(ActionEvent e)
+        {
+            configurationChooser.setDialogTitle(msg("saveConfigurationFile"));
+
+            int returnVal = configurationChooser.showSaveDialog(ProGuardGUI.this);
+            if (returnVal == JFileChooser.APPROVE_OPTION)
+            {
+                File selectedFile = configurationChooser.getSelectedFile();
+                String fileName = selectedFile.getPath();
+
+                saveConfiguration(fileName);
+            }
+        }
+    }
+
+
+    /**
+     * This ActionListener displays the ProGuard configuration specified by the
+     * current GUI settings.
+     */
+    private class MyViewConfigurationActionListener implements ActionListener
+    {
+        public void actionPerformed(ActionEvent e)
+        {
+            // Make sure System.out has not been redirected yet.
+            if (!systemOutRedirected)
+            {
+                consoleTextArea.setText("");
+
+                TextAreaOutputStream outputStream =
+                    new TextAreaOutputStream(consoleTextArea);
+
+                try
+                {
+                    // TODO: write out relative path names and path names with system
+                    // properties.
+
+                    // Write the configuration.
+                    ConfigurationWriter writer = new ConfigurationWriter(outputStream);
+                    writer.write(getProGuardConfiguration());
+                    writer.close();
+                }
+                catch (IOException ex)
+                {
+                }
+
+                // Scroll to the top of the configuration.
+                consoleTextArea.setCaretPosition(0);
+            }
+        }
+    }
+
+
+    /**
+     * This ActionListener executes ProGuard based on the current GUI settings.
+     */
+    private class MyProcessActionListener implements ActionListener
+    {
+        public void actionPerformed(ActionEvent e)
+        {
+            // Make sure System.out has not been redirected yet.
+            if (!systemOutRedirected)
+            {
+                systemOutRedirected = true;
+
+                // Get the informational configuration file name.
+                File configurationFile = configurationChooser.getSelectedFile();
+                String configurationFileName = configurationFile != null ?
+                    configurationFile.getName() :
+                    msg("sampleConfigurationFileName");
+
+                // Create the ProGuard thread.
+                Thread proGuardThread =
+                    new Thread(new ProGuardRunnable(consoleTextArea,
+                                                    getProGuardConfiguration(),
+                                                    configurationFileName));
+
+                // Run it.
+                proGuardThread.start();
+            }
+        }
+    }
+
+
+    /**
+     * This ActionListener loads an obfuscated stack trace from a file and puts
+     * it in the proper text area.
+     */
+    private class MyLoadStackTraceActionListener implements ActionListener
+    {
+        public void actionPerformed(ActionEvent e)
+        {
+            fileChooser.setDialogTitle(msg("selectStackTraceFile"));
+            fileChooser.setSelectedFile(null);
+
+            int returnValue = fileChooser.showOpenDialog(ProGuardGUI.this);
+            if (returnValue == JFileChooser.APPROVE_OPTION)
+            {
+                File selectedFile = fileChooser.getSelectedFile();
+                String fileName = selectedFile.getPath();
+
+                loadStackTrace(fileName);
+            }
+        }
+    }
+
+
+    /**
+     * This ActionListener executes ReTrace based on the current GUI settings.
+     */
+    private class MyReTraceActionListener implements ActionListener
+    {
+        public void actionPerformed(ActionEvent e)
+        {
+            // Make sure System.out has not been redirected yet.
+            if (!systemOutRedirected)
+            {
+                systemOutRedirected = true;
+
+                boolean verbose            = reTraceVerboseCheckBox.isSelected();
+                String  retraceMappingFile = reTraceMappingTextField.getText();
+                String  stackTrace         = stackTraceTextArea.getText();
+
+                // Create the ReTrace runnable.
+                Runnable reTraceRunnable = new ReTraceRunnable(reTraceTextArea,
+                                                               verbose,
+                                                               retraceMappingFile,
+                                                               stackTrace);
+
+                // Run it in this thread, because it won't take long anyway.
+                reTraceRunnable.run();
+            }
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns the message from the GUI resources that corresponds to the given
+     * key.
+     */
+    private String msg(String messageKey)
+    {
+         return GUIResources.getMessage(messageKey);
+    }
+
+
+    /**
+     * Returns the message from the GUI resources that corresponds to the given
+     * key and argument.
+     */
+    private String msg(String messageKey,
+                       Object messageArgument)
+    {
+         return GUIResources.getMessage(messageKey, new Object[] {messageArgument});
+    }
+
+
+    /**
+     * The main method for the ProGuard GUI.
+     */
+    public static void main(String[] args)
+    {
+        ProGuardGUI gui = new ProGuardGUI();
+        gui.pack();
+
+        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+        Dimension guiSize    = gui.getSize();
+        gui.setLocation((screenSize.width - guiSize.width)   / 2,
+                        (screenSize.height - guiSize.height) / 2);
+        gui.show();
+
+        // Start the splash animation, unless specified otherwise.
+        int argIndex = 0;
+        if (argIndex < args.length &&
+            NO_SPLASH_OPTION.startsWith(args[argIndex]))
+        {
+            gui.skipSplash();
+            argIndex++;
+        }
+        else
+        {
+            gui.startSplash();
+        }
+
+        // Load an initial configuration, if specified.
+        if (argIndex < args.length)
+        {
+            gui.loadConfiguration(args[argIndex]);
+            argIndex++;
+        }
+
+        if (argIndex < args.length)
+        {
+            System.out.println(gui.getClass().getName() + ": ignoring extra arguments [" + args[argIndex] + "...]");
+        }
+    }
+}
diff --git a/src/proguard/gui/ProGuardRunnable.java b/src/proguard/gui/ProGuardRunnable.java
new file mode 100644
index 0000000..ad5f31b
--- /dev/null
+++ b/src/proguard/gui/ProGuardRunnable.java
@@ -0,0 +1,148 @@
+/* $Id: ProGuardRunnable.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui;
+
+import java.awt.*;
+import java.io.*;
+
+import javax.swing.*;
+
+import proguard.*;
+
+
+/**
+ * This <code>Runnable</code> runs ProGuard, sending console output to a text
+ * area and any exceptions to message dialogs.
+ *
+ * @see ProGuard
+ * @author Eric Lafortune
+ */
+class ProGuardRunnable implements Runnable
+{
+    private JTextArea     consoleTextArea;
+    private Configuration configuration;
+    private String        configurationFileName;
+
+
+    /**
+     * Creates a new ProGuardRunnable object.
+     * @param consoleTextArea       the text area to send the console output to.
+     * @param configuration         the ProGuard configuration.
+     * @param configurationFileName the optional file name of the configuration,
+     *                              for informational purposes.
+     */
+    public ProGuardRunnable(JTextArea     consoleTextArea,
+                            Configuration configuration,
+                            String        configurationFileName)
+    {
+        this.consoleTextArea       = consoleTextArea;
+        this.configuration         = configuration;
+        this.configurationFileName = configurationFileName;
+    }
+
+
+    // Implementation for Runnable.
+
+    public void run()
+    {
+        consoleTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+        consoleTextArea.setText("");
+
+        // Redirect the System's out and err streams to the console text area.
+        PrintStream oldOut = System.out;
+        PrintStream oldErr = System.err;
+
+        PrintStream printStream =
+            new PrintStream(new TextAreaOutputStream(consoleTextArea), true);
+
+        System.setOut(printStream);
+        System.setErr(printStream);
+
+        try
+        {
+            // Create a new ProGuard object with the GUI's configuration.
+            ProGuard proGuard = new ProGuard(configuration);
+
+            // Run it.
+            proGuard.execute();
+        }
+        catch (Exception ex)
+        {
+            // Print out the exception message.
+            System.out.println(ex.getMessage());
+
+            // Show a dialog as well.
+            MessageDialogRunnable.showMessageDialog(consoleTextArea,
+                                                    ex.getMessage(),
+                                                    msg("errorProcessing"),
+                                                    JOptionPane.ERROR_MESSAGE);
+        }
+        catch (OutOfMemoryError er)
+        {
+            // Forget about the ProGuard object as quickly as possible.
+            System.gc();
+
+            // Print out a message suggesting what to do next.
+            System.out.println(msg("outOfMemoryInfo", configurationFileName));
+
+            // Show a dialog as well.
+            MessageDialogRunnable.showMessageDialog(consoleTextArea,
+                                                    msg("outOfMemory"),
+                                                    msg("errorProcessing"),
+                                                    JOptionPane.ERROR_MESSAGE);
+        }
+
+        // Make sure all output has been sent to the console text area.
+        printStream.flush();
+
+        // Restore the old System's out and err streams.
+        System.setOut(oldOut);
+        System.setErr(oldErr);
+
+        consoleTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+
+        // Reset the global static redirection lock.
+        ProGuardGUI.systemOutRedirected = false;
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns the message from the GUI resources that corresponds to the given
+     * key.
+     */
+    private String msg(String messageKey)
+    {
+         return GUIResources.getMessage(messageKey);
+    }
+
+
+    /**
+     * Returns the message from the GUI resources that corresponds to the given
+     * key and argument.
+     */
+    private String msg(String messageKey,
+                       Object messageArgument)
+    {
+         return GUIResources.getMessage(messageKey, new Object[] {messageArgument});
+    }
+}
diff --git a/src/proguard/gui/ReTraceRunnable.java b/src/proguard/gui/ReTraceRunnable.java
new file mode 100644
index 0000000..29cca05
--- /dev/null
+++ b/src/proguard/gui/ReTraceRunnable.java
@@ -0,0 +1,149 @@
+/* $Id: ReTraceRunnable.java,v 1.6 2004/08/21 21:36:03 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui;
+
+import java.awt.Cursor;
+import java.io.*;
+
+import javax.swing.*;
+
+import proguard.retrace.ReTrace;
+
+
+/**
+ * This <code>Runnable</code> runs ReTrace, sending console output to a text
+ * area and any exceptions to message dialogs.
+ *
+ * @see ReTrace
+ * @author Eric Lafortune
+ */
+class ReTraceRunnable implements Runnable
+{
+    private JTextArea consoleTextArea;
+    private boolean   verbose;
+    private String    retraceMappingFile;
+    private String    stackTrace;
+
+
+    /**
+     * Creates a new ProGuardRunnable object.
+     * @param consoleTextArea the text area to send the console output to.
+     * @param verbose         specifies whether the de-obfuscated stack trace
+     *                        should be verbose.
+     * @param mappingFileName the mapping file that was written out by ProGuard.
+     */
+    public ReTraceRunnable(JTextArea consoleTextArea,
+                           boolean   verbose,
+                           String    retraceMappingFile,
+                           String    stackTrace)
+    {
+        this.consoleTextArea    = consoleTextArea;
+        this.verbose            = verbose;
+        this.retraceMappingFile = retraceMappingFile;
+        this.stackTrace         = stackTrace;
+    }
+
+
+    // Implementation for Runnable.
+
+    public void run()
+    {
+        consoleTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+        consoleTextArea.setText("");
+
+        // Redirect the stack trace string to the System's in stream, and the
+        // out and err streams to the console text area.
+        InputStream oldIn  = System.in;
+        PrintStream oldOut = System.out;
+        PrintStream oldErr = System.err;
+
+        ByteArrayInputStream inputStream =
+           new ByteArrayInputStream(stackTrace.getBytes());
+
+        PrintStream printStream =
+            new PrintStream(new TextAreaOutputStream(consoleTextArea), true);
+
+        System.setIn(inputStream);
+        System.setOut(printStream);
+        System.setErr(printStream);
+
+        try
+        {
+            // Create a new ProGuard object with the GUI's configuration.
+            ReTrace reTrace = new ReTrace(verbose,
+                                          retraceMappingFile);
+
+            // Run it.
+            reTrace.execute();
+        }
+        catch (Exception ex)
+        {
+            // Print out the exception message.
+            System.out.println(ex.getMessage());
+
+            // Show a dialog as well.
+            MessageDialogRunnable.showMessageDialog(consoleTextArea,
+                                                    ex.getMessage(),
+                                                    msg("errorReTracing"),
+                                                    JOptionPane.ERROR_MESSAGE);
+        }
+        catch (OutOfMemoryError er)
+        {
+            // Forget about the ProGuard object as quickly as possible.
+            System.gc();
+
+            // Print out a message suggesting what to do next.
+            System.out.println(msg("outOfMemory"));
+
+            // Show a dialog as well.
+            MessageDialogRunnable.showMessageDialog(consoleTextArea,
+                                                    msg("outOfMemory"),
+                                                    msg("errorReTracing"),
+                                                    JOptionPane.ERROR_MESSAGE);
+        }
+
+        // Make sure all output has been sent to the console text area.
+        printStream.flush();
+
+        // Restore the old System's in, out, and err streams.
+        System.setIn(oldIn);
+        System.setOut(oldOut);
+        System.setErr(oldErr);
+
+        consoleTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+        consoleTextArea.setCaretPosition(0);
+
+        // Reset the global static redirection lock.
+        ProGuardGUI.systemOutRedirected = false;
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns the message from the GUI resources that corresponds to the given
+     * key.
+     */
+    private String msg(String messageKey)
+    {
+         return GUIResources.getMessage(messageKey);
+    }
+}
diff --git a/src/proguard/gui/SwingUtil.java b/src/proguard/gui/SwingUtil.java
new file mode 100644
index 0000000..22f3da4
--- /dev/null
+++ b/src/proguard/gui/SwingUtil.java
@@ -0,0 +1,80 @@
+/* $Id: SwingUtil.java,v 1.4 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui;
+
+import javax.swing.*;
+
+
+/**
+ * This utility class provides variants of the invocation method from the
+ * <code>SwingUtilities</code> class.
+ *
+ * @see SwingUtilities
+ * @author Eric Lafortune
+ */
+class SwingUtil
+{
+    /**
+     * Invokes the given Runnable in the AWT event dispatching thread,
+     * and waits for it to finish. This method may be called from any thread,
+     * including the event dispatching thread itself.
+     * @see SwingUtilities#invokeAndWait(java.lang.Runnable)
+     * @param runnable the Runnable to be executed.
+     */
+    public static void invokeAndWait(Runnable runnable)
+    {
+        try
+        {
+            if (SwingUtilities.isEventDispatchThread())
+            {
+                runnable.run();
+            }
+            else
+            {
+                SwingUtilities.invokeAndWait(runnable);
+            }
+        }
+        catch (Exception ex)
+        {
+            // Ignore any exceptions.
+        }
+    }
+
+
+    /**
+     * Invokes the given Runnable in the AWT event dispatching thread, not
+     * necessarily right away. This method may be called from any thread,
+     * including the event dispatching thread itself.
+     * @see SwingUtilities#invokeLater(java.lang.Runnable)
+     * @param runnable the Runnable to be executed.
+     */
+    public static void invokeLater(Runnable runnable)
+    {
+        if (SwingUtilities.isEventDispatchThread())
+        {
+            runnable.run();
+        }
+        else
+        {
+            SwingUtilities.invokeLater(runnable);
+        }
+    }
+}
diff --git a/src/proguard/gui/TabbedPane.java b/src/proguard/gui/TabbedPane.java
new file mode 100644
index 0000000..e61c8ba
--- /dev/null
+++ b/src/proguard/gui/TabbedPane.java
@@ -0,0 +1,229 @@
+/* $Id: TabbedPane.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+
+
+/**
+ * This <code>Jpanel</code> is similar to a <code>JTabbedPane</code>.
+ * It uses buttons on the left-hand side to switch between panels.
+ * An image can be added below these buttons.
+ * Some methods are provided to switch between tabs.
+ *
+ * @author Eric Lafortune
+ */
+public class TabbedPane
+     extends JPanel
+{
+    private CardLayout  cardLayout  = new CardLayout();
+    private JPanel      cardPanel   = new JPanel(cardLayout);
+    private ButtonGroup buttonGroup = new ButtonGroup();
+
+
+    /**
+     * Creates a new TabbedPane.
+     */
+    public TabbedPane()
+    {
+        GridBagLayout layout = new GridBagLayout();
+        setLayout(layout);
+
+        GridBagConstraints cardConstraints = new GridBagConstraints();
+        cardConstraints.gridx      = 1;
+        cardConstraints.gridy      = 0;
+        cardConstraints.gridheight = GridBagConstraints.REMAINDER;
+        cardConstraints.fill       = GridBagConstraints.BOTH;
+        cardConstraints.weightx    = 1.0;
+        cardConstraints.weighty    = 1.0;
+        cardConstraints.anchor     = GridBagConstraints.NORTHWEST;
+
+        add(cardPanel, cardConstraints);
+    }
+
+
+    /**
+     * Adds a component with a given title to the tabbed pane.
+     *
+     * @param title     the title that will be used in the tab button.
+     * @param component the component that will be added as a tab.
+     */
+    public Component add(final String title, Component component)
+    {
+        GridBagConstraints buttonConstraints = new GridBagConstraints();
+        buttonConstraints.gridx  = 0;
+        buttonConstraints.fill   = GridBagConstraints.HORIZONTAL;
+        buttonConstraints.anchor = GridBagConstraints.NORTHWEST;
+        buttonConstraints.ipadx  = 10;
+        buttonConstraints.ipady  = 4;
+
+        JToggleButton button = new JToggleButton(title);
+
+        // Let the button react on the mouse press, instead of waiting for the
+        // mouse release.
+        button.setModel(new JToggleButton.ToggleButtonModel()
+        {
+            public void setPressed(boolean b)
+            {
+                if ((isPressed() == b) || !isEnabled())
+                {
+                    return;
+                }
+
+                if (b == false && isArmed())
+                {
+                    setSelected(!this.isSelected());
+                }
+
+                if (b)
+                {
+                    stateMask |= PRESSED;
+                }
+                else
+                {
+                    stateMask &= ~PRESSED;
+                }
+
+                fireStateChanged();
+
+                if (isPressed())
+                {
+                    fireActionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, getActionCommand()));
+                }
+            }
+
+        });
+
+        // Switch to the tab on a button press.
+        button.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                cardLayout.show(cardPanel, title);
+            }
+        });
+
+        // Only one button can be selected at the same time.
+        buttonGroup.add(button);
+
+        // If this is the first tab, make sure its button is selected.
+        if (cardPanel.getComponentCount() == 0)
+        {
+            button.setSelected(true);
+        }
+
+        // Add the button and its panel.
+        add(button, buttonConstraints);
+        cardPanel.add(title, component);
+
+        return component;
+    }
+
+
+    /**
+     * Adds an image below the tab buttons, after all tabs have been added.
+     * The image will only be as visible as permitted by the available space.
+     *
+     * @param image the image.
+     * @return the component containing the image.
+     */
+    public Component addImage(final Image image)
+    {
+        GridBagConstraints imageConstraints = new GridBagConstraints();
+        imageConstraints.gridx   = 0;
+        imageConstraints.weighty = 1.0;
+        imageConstraints.fill    = GridBagConstraints.BOTH;
+        imageConstraints.anchor  = GridBagConstraints.SOUTHWEST;
+
+        JPanel component = new JPanel()
+        {
+            public void paintComponent(Graphics graphics)
+            {
+                graphics.drawImage(image, 0, getHeight() - image.getHeight(null), this);
+            }
+        };
+        component.setBorder(BorderFactory.createEtchedBorder());
+
+        add(component, imageConstraints);
+
+        return component;
+    }
+
+
+    /**
+     * Selects the first tab.
+     */
+    public void first()
+    {
+        cardLayout.first(cardPanel);
+        updateButtonSelection();
+    }
+
+
+    /**
+     * Selects the last tab.
+     */
+    public void last()
+    {
+        cardLayout.last(cardPanel);
+        updateButtonSelection();
+    }
+
+
+    /**
+     * Selects the previous tab.
+     */
+    public void previous()
+    {
+        cardLayout.previous(cardPanel);
+        updateButtonSelection();
+    }
+
+
+    /**
+     * Selects the next tab.
+     */
+    public void next()
+    {
+        cardLayout.next(cardPanel);
+        updateButtonSelection();
+    }
+
+
+    /**
+     * Lets the button selection reflect the currently visible panel.
+     */
+    private void updateButtonSelection()
+    {
+        int count = cardPanel.getComponentCount();
+        for (int index = 0 ; index < count ; index++) {
+            Component card = cardPanel.getComponent(index);
+            if (card.isShowing())
+            {
+                JToggleButton button = (JToggleButton)getComponent(index+1);
+                button.setSelected(true);
+            }
+        }
+    }
+}
diff --git a/src/proguard/gui/TextAreaOutputStream.java b/src/proguard/gui/TextAreaOutputStream.java
new file mode 100644
index 0000000..e06df07
--- /dev/null
+++ b/src/proguard/gui/TextAreaOutputStream.java
@@ -0,0 +1,74 @@
+/* $Id: TextAreaOutputStream.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui;
+
+import java.io.*;
+
+import javax.swing.*;
+
+
+/**
+ * This <code>PrintStream</code> appends its output to a given text area.
+ *
+ * @author Eric Lafortune
+ */
+class TextAreaOutputStream extends FilterOutputStream implements Runnable
+{
+    private JTextArea textArea;
+
+
+    public TextAreaOutputStream(JTextArea textArea)
+    {
+        super(new ByteArrayOutputStream());
+
+        this.textArea = textArea;
+    }
+
+
+    // Implementation for FilterOutputStream.
+
+    public void flush() throws IOException
+    {
+        super.flush();
+
+        // Append the accumulated buffer contents to the text area.
+        SwingUtil.invokeAndWait(this);
+    }
+
+
+    // Implementation for Runnable.
+
+    public synchronized void run()
+    {
+        ByteArrayOutputStream out = (ByteArrayOutputStream)super.out;
+
+        // Has any new text been written?
+        String text = out.toString();
+        if (text.length() > 0)
+        {
+            // Append the accumulated text to the text area.
+            textArea.append(text);
+
+            // Clear the buffer.
+            out.reset();
+        }
+    }
+}
diff --git a/src/proguard/gui/arrow.gif b/src/proguard/gui/arrow.gif
new file mode 100644
index 0000000..c58e34e
Binary files /dev/null and b/src/proguard/gui/arrow.gif differ
diff --git a/src/proguard/gui/boilerplate.pro b/src/proguard/gui/boilerplate.pro
new file mode 100644
index 0000000..c905e24
--- /dev/null
+++ b/src/proguard/gui/boilerplate.pro
@@ -0,0 +1,277 @@
+# Keep - Applications. Keep all application classes that have a main method.
+-keepclasseswithmembers public class * {
+    public static void main(java.lang.String[]);
+}
+
+# Keep - Applets. Keep all extensions of java.applet.Applet.
+-keep public class * extends java.applet.Applet
+
+# Keep - Servlets. Keep all extensions of javax.servlet.Servlet.
+-keep public class * extends javax.servlet.Servlet
+
+# Keep - Midlets. Keep all extensions of javax.microedition.midlet.MIDlet.
+-keep public class * extends javax.microedition.midlet.MIDlet
+
+# Keep - Library. Keep all externally accessible classes, fields, and methods.
+-keep public class * {
+    public protected <fields>;
+    public protected <methods>;
+}
+
+# Also keep - Enumerations. Keep a method that is required in enumeration
+# classes.
+-keepclassmembers class * extends java.lang.Enum {
+    public **[] values();
+}
+
+# Also keep - Serialization code. Keep all fields and methods that are
+# used for serialization.
+-keepclassmembers class * extends java.io.Serializable {
+    static final long serialVersionUID;
+    private void writeObject(java.io.ObjectOutputStream);
+    private void readObject(java.io.ObjectInputStream);
+    java.lang.Object writeReplace();
+    java.lang.Object readResolve();
+}
+
+# Also keep - BeanInfo classes. Keep all classes that implement the
+# BeanInfo interface.
+-keep class * implements java.beans.BeanInfo
+
+# Also keep - Bean classes. Keep all bean classes along with their getters
+# and setters.
+-keep class * {
+    void set*(%);
+    void set*(**);
+    void set*(%[]);
+    void set*(**[]);
+    void set*(int, %);
+    void set*(int, **);
+
+    %    get*();
+    **   get*();
+    %[]  get*();
+    **[] get*();
+    %    get*(int);
+    **   get*(int);
+}
+
+# Also keep - RMI interfaces. Keep all Remote interfaces and their methods.
+-keep interface * extends java.rmi.Remote {
+    <methods>;
+}
+
+# Also keep - RMI implementations. Keep all Remote implementations. This
+# includes any explicit or implicit Activatable implementations with their
+# two-argument constructors.
+-keep class * implements java.rmi.Remote {
+    <init>(java.rmi.activation.ActivationID, java.rmi.MarshalledObject);
+}
+
+# Keep names - Native method names. Keep all native class/method names.
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+# Keep names - _class method names. Keep all .class method names. Useful for
+# libraries that will be obfuscated again.
+-keepclassmembernames class * {
+    java.lang.Class class$(java.lang.String);
+    java.lang.Class class$(java.lang.String, boolean);
+}
+
+# Remove - System method calls. Remove all invocations of System
+# methods without side effects whose return values are not used.
+-assumenosideeffects public class java.lang.System {
+    public static native long currentTimeMillis();
+    static java.lang.Class getCallerClass();
+    public static native int identityHashCode(java.lang.Object);
+    public static java.lang.SecurityManager getSecurityManager();
+    public static java.util.Properties getProperties();
+    public static java.lang.String getProperty(java.lang.String);
+    public static java.lang.String getenv(java.lang.String);
+    public static native java.lang.String mapLibraryName(java.lang.String);
+    public static java.lang.String getProperty(java.lang.String,java.lang.String);
+}
+
+# Remove - Math method calls. Remove all invocations of Math
+# methods without side effects whose return values are not used.
+-assumenosideeffects public class java.lang.Math {
+    public static double sin(double);
+    public static double cos(double);
+    public static double tan(double);
+    public static double asin(double);
+    public static double acos(double);
+    public static double atan(double);
+    public static double toRadians(double);
+    public static double toDegrees(double);
+    public static double exp(double);
+    public static double log(double);
+    public static double log10(double);
+    public static double sqrt(double);
+    public static double cbrt(double);
+    public static double IEEEremainder(double, double);
+    public static double ceil(double);
+    public static double floor(double);
+    public static double rint(double);
+    public static double atan2(double, double);
+    public static double pow(double, double);
+    public static int round(float);
+    public static long round(double);
+    public static double random();
+    public static int abs(int);
+    public static long abs(long);
+    public static float abs(float);
+    public static double abs(double);
+    public static int max(int, int);
+    public static long max(long, long);
+    public static float max(float, float);
+    public static double max(double, double);
+    public static int min(int, int);
+    public static long min(long, long);
+    public static float min(float, float);
+    public static double min(double, double);
+    public static double ulp(double);
+    public static float ulp(float);
+    public static double signum(double);
+    public static float signum(float);
+    public static double sinh(double);
+    public static double cosh(double);
+    public static double tanh(double);
+    public static double hypot(double, double);
+    public static double expm1(double);
+    public static double log1p(double);
+}
+
+# Remove - String method calls. Remove all invocations of String
+# methods without side effects whose return values are not used.
+-assumenosideeffects public class java.lang.String {
+    public java.lang.String();
+    public java.lang.String(byte[]);
+    public java.lang.String(byte[],int);
+    public java.lang.String(byte[],int,int);
+    public java.lang.String(byte[],int,int,int);
+    public java.lang.String(byte[],int,int,java.lang.String);
+    public java.lang.String(byte[],java.lang.String);
+    public java.lang.String(char[]);
+    public java.lang.String(char[],int,int);
+    public java.lang.String(java.lang.String);
+    public java.lang.String(java.lang.StringBuffer);
+    public static java.lang.String copyValueOf(char[]);
+    public static java.lang.String copyValueOf(char[],int,int);
+    public static java.lang.String valueOf(boolean);
+    public static java.lang.String valueOf(char);
+    public static java.lang.String valueOf(char[]);
+    public static java.lang.String valueOf(char[],int,int);
+    public static java.lang.String valueOf(double);
+    public static java.lang.String valueOf(float);
+    public static java.lang.String valueOf(int);
+    public static java.lang.String valueOf(java.lang.Object);
+    public static java.lang.String valueOf(long);
+    public boolean contentEquals(java.lang.StringBuffer);
+    public boolean endsWith(java.lang.String);
+    public boolean equalsIgnoreCase(java.lang.String);
+    public boolean equals(java.lang.Object);
+    public boolean matches(java.lang.String);
+    public boolean regionMatches(boolean,int,java.lang.String,int,int);
+    public boolean regionMatches(int,java.lang.String,int,int);
+    public boolean startsWith(java.lang.String);
+    public boolean startsWith(java.lang.String,int);
+    public byte[] getBytes();
+    public byte[] getBytes(java.lang.String);
+    public char charAt(int);
+    public char[] toCharArray();
+    public int compareToIgnoreCase(java.lang.String);
+    public int compareTo(java.lang.Object);
+    public int compareTo(java.lang.String);
+    public int hashCode();
+    public int indexOf(int);
+    public int indexOf(int,int);
+    public int indexOf(java.lang.String);
+    public int indexOf(java.lang.String,int);
+    public int lastIndexOf(int);
+    public int lastIndexOf(int,int);
+    public int lastIndexOf(java.lang.String);
+    public int lastIndexOf(java.lang.String,int);
+    public int length();
+    public java.lang.CharSequence subSequence(int,int);
+    public java.lang.String concat(java.lang.String);
+    public java.lang.String replaceAll(java.lang.String,java.lang.String);
+    public java.lang.String replace(char,char);
+    public java.lang.String replaceFirst(java.lang.String,java.lang.String);
+    public java.lang.String[] split(java.lang.String);
+    public java.lang.String[] split(java.lang.String,int);
+    public java.lang.String substring(int);
+    public java.lang.String substring(int,int);
+    public java.lang.String toLowerCase();
+    public java.lang.String toLowerCase(java.util.Locale);
+    public java.lang.String toString();
+    public java.lang.String toUpperCase();
+    public java.lang.String toUpperCase(java.util.Locale);
+    public java.lang.String trim();
+}
+
+
+# Remove - StringBuffer method calls. Remove all invocations of
+# StringBuffer methods without side effects whose return values are not used.
+-assumenosideeffects public class java.lang.StringBuffer {
+    public java.lang.StringBuffer();
+    public java.lang.StringBuffer(int);
+    public java.lang.StringBuffer(java.lang.String);
+    public java.lang.StringBuffer(java.lang.CharSequence);
+    public java.lang.String toString();
+    public char charAt(int);
+    public int capacity();
+    public int codePointAt(int);
+    public int codePointBefore(int);
+    public int indexOf(java.lang.String,int);
+    public int lastIndexOf(java.lang.String);
+    public int lastIndexOf(java.lang.String,int);
+    public int length();
+    public java.lang.String substring(int);
+    public java.lang.String substring(int,int);
+}
+
+# Remove - StringBuilder method calls. Remove all invocations of
+# StringBuilder methods without side effects whose return values are not used.
+-assumenosideeffects public class java.lang.StringBuilder {
+    public java.lang.StringBuilder();
+    public java.lang.StringBuilder(int);
+    public java.lang.StringBuilder(java.lang.String);
+    public java.lang.StringBuilder(java.lang.CharSequence);
+    public java.lang.String toString();
+    public char charAt(int);
+    public int capacity();
+    public int codePointAt(int);
+    public int codePointBefore(int);
+    public int indexOf(java.lang.String,int);
+    public int lastIndexOf(java.lang.String);
+    public int lastIndexOf(java.lang.String,int);
+    public int length();
+    public java.lang.String substring(int);
+    public java.lang.String substring(int,int);
+}
+
+# Remove debugging - Throwable_printStackTrace calls. Remove all invocations of
+# Throwable.printStackTrace().
+-assumenosideeffects public class java.lang.Throwable {
+    public void printStackTrace();
+}
+
+# Remove debugging - Thread_dumpStack calls. Remove all invocations of
+# Thread.dumpStack().
+-assumenosideeffects public class java.lang.Thread {
+    public static void dumpStack();
+}
+
+# Remove debugging - All logging API calls. Remove all invocations of the
+# logging API whose return values are not used.
+-assumenosideeffects public class java.util.logging.* {
+    <methods>;
+}
+
+# Remove debugging - All Log4j API calls. Remove all invocations of the
+# Log4j API whose return values are not used.
+-assumenosideeffects public class org.apache.log4j.** {
+    <methods>;
+}
diff --git a/src/proguard/gui/default.pro b/src/proguard/gui/default.pro
new file mode 100644
index 0000000..a8cf902
--- /dev/null
+++ b/src/proguard/gui/default.pro
@@ -0,0 +1,193 @@
+# The default configuration when starting up the GUI.
+
+-libraryjars <java.home>/lib/rt.jar
+
+-verbose
+
+# Keep - Applications. Keep all application classes that have a main method.
+-keepclasseswithmembers public class * {
+    public static void main(java.lang.String[]);
+}
+
+# Keep names - Native method names. Keep all native class/method names.
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+# Also keep - Enumerations. Keep a method that is required in enumeration
+# classes.
+-keepclassmembers class * extends java.lang.Enum {
+    public **[] values();
+}
+
+# Remove - System method calls. Remove all invocations of System
+# methods without side effects whose return values are not used.
+-assumenosideeffects public class java.lang.System {
+    public static native long currentTimeMillis();
+    static java.lang.Class getCallerClass();
+    public static native int identityHashCode(java.lang.Object);
+    public static java.lang.SecurityManager getSecurityManager();
+    public static java.util.Properties getProperties();
+    public static java.lang.String getProperty(java.lang.String);
+    public static java.lang.String getenv(java.lang.String);
+    public static native java.lang.String mapLibraryName(java.lang.String);
+    public static java.lang.String getProperty(java.lang.String,java.lang.String);
+}
+
+# Remove - Math method calls. Remove all invocations of Math
+# methods without side effects whose return values are not used.
+-assumenosideeffects public class java.lang.Math {
+    public static double sin(double);
+    public static double cos(double);
+    public static double tan(double);
+    public static double asin(double);
+    public static double acos(double);
+    public static double atan(double);
+    public static double toRadians(double);
+    public static double toDegrees(double);
+    public static double exp(double);
+    public static double log(double);
+    public static double log10(double);
+    public static double sqrt(double);
+    public static double cbrt(double);
+    public static double IEEEremainder(double, double);
+    public static double ceil(double);
+    public static double floor(double);
+    public static double rint(double);
+    public static double atan2(double, double);
+    public static double pow(double, double);
+    public static int round(float);
+    public static long round(double);
+    public static double random();
+    public static int abs(int);
+    public static long abs(long);
+    public static float abs(float);
+    public static double abs(double);
+    public static int max(int, int);
+    public static long max(long, long);
+    public static float max(float, float);
+    public static double max(double, double);
+    public static int min(int, int);
+    public static long min(long, long);
+    public static float min(float, float);
+    public static double min(double, double);
+    public static double ulp(double);
+    public static float ulp(float);
+    public static double signum(double);
+    public static float signum(float);
+    public static double sinh(double);
+    public static double cosh(double);
+    public static double tanh(double);
+    public static double hypot(double, double);
+    public static double expm1(double);
+    public static double log1p(double);
+}
+
+# Remove - String method calls. Remove all invocations of String
+# methods without side effects whose return values are not used.
+-assumenosideeffects public class java.lang.String {
+    public java.lang.String();
+    public java.lang.String(byte[]);
+    public java.lang.String(byte[],int);
+    public java.lang.String(byte[],int,int);
+    public java.lang.String(byte[],int,int,int);
+    public java.lang.String(byte[],int,int,java.lang.String);
+    public java.lang.String(byte[],java.lang.String);
+    public java.lang.String(char[]);
+    public java.lang.String(char[],int,int);
+    public java.lang.String(java.lang.String);
+    public java.lang.String(java.lang.StringBuffer);
+    public static java.lang.String copyValueOf(char[]);
+    public static java.lang.String copyValueOf(char[],int,int);
+    public static java.lang.String valueOf(boolean);
+    public static java.lang.String valueOf(char);
+    public static java.lang.String valueOf(char[]);
+    public static java.lang.String valueOf(char[],int,int);
+    public static java.lang.String valueOf(double);
+    public static java.lang.String valueOf(float);
+    public static java.lang.String valueOf(int);
+    public static java.lang.String valueOf(java.lang.Object);
+    public static java.lang.String valueOf(long);
+    public boolean contentEquals(java.lang.StringBuffer);
+    public boolean endsWith(java.lang.String);
+    public boolean equalsIgnoreCase(java.lang.String);
+    public boolean equals(java.lang.Object);
+    public boolean matches(java.lang.String);
+    public boolean regionMatches(boolean,int,java.lang.String,int,int);
+    public boolean regionMatches(int,java.lang.String,int,int);
+    public boolean startsWith(java.lang.String);
+    public boolean startsWith(java.lang.String,int);
+    public byte[] getBytes();
+    public byte[] getBytes(java.lang.String);
+    public char charAt(int);
+    public char[] toCharArray();
+    public int compareToIgnoreCase(java.lang.String);
+    public int compareTo(java.lang.Object);
+    public int compareTo(java.lang.String);
+    public int hashCode();
+    public int indexOf(int);
+    public int indexOf(int,int);
+    public int indexOf(java.lang.String);
+    public int indexOf(java.lang.String,int);
+    public int lastIndexOf(int);
+    public int lastIndexOf(int,int);
+    public int lastIndexOf(java.lang.String);
+    public int lastIndexOf(java.lang.String,int);
+    public int length();
+    public java.lang.CharSequence subSequence(int,int);
+    public java.lang.String concat(java.lang.String);
+    public java.lang.String replaceAll(java.lang.String,java.lang.String);
+    public java.lang.String replace(char,char);
+    public java.lang.String replaceFirst(java.lang.String,java.lang.String);
+    public java.lang.String[] split(java.lang.String);
+    public java.lang.String[] split(java.lang.String,int);
+    public java.lang.String substring(int);
+    public java.lang.String substring(int,int);
+    public java.lang.String toLowerCase();
+    public java.lang.String toLowerCase(java.util.Locale);
+    public java.lang.String toString();
+    public java.lang.String toUpperCase();
+    public java.lang.String toUpperCase(java.util.Locale);
+    public java.lang.String trim();
+}
+
+
+# Remove - StringBuffer method calls. Remove all invocations of
+# StringBuffer methods without side effects whose return values are not used.
+-assumenosideeffects public class java.lang.StringBuffer {
+    public java.lang.StringBuffer();
+    public java.lang.StringBuffer(int);
+    public java.lang.StringBuffer(java.lang.String);
+    public java.lang.StringBuffer(java.lang.CharSequence);
+    public java.lang.String toString();
+    public char charAt(int);
+    public int capacity();
+    public int codePointAt(int);
+    public int codePointBefore(int);
+    public int indexOf(java.lang.String,int);
+    public int lastIndexOf(java.lang.String);
+    public int lastIndexOf(java.lang.String,int);
+    public int length();
+    public java.lang.String substring(int);
+    public java.lang.String substring(int,int);
+}
+
+# Remove - StringBuilder method calls. Remove all invocations of
+# StringBuilder methods without side effects whose return values are not used.
+-assumenosideeffects public class java.lang.StringBuilder {
+    public java.lang.StringBuilder();
+    public java.lang.StringBuilder(int);
+    public java.lang.StringBuilder(java.lang.String);
+    public java.lang.StringBuilder(java.lang.CharSequence);
+    public java.lang.String toString();
+    public char charAt(int);
+    public int capacity();
+    public int codePointAt(int);
+    public int codePointBefore(int);
+    public int indexOf(java.lang.String,int);
+    public int lastIndexOf(java.lang.String);
+    public int lastIndexOf(java.lang.String,int);
+    public int length();
+    public java.lang.String substring(int);
+    public java.lang.String substring(int,int);
+}
diff --git a/src/proguard/gui/package.html b/src/proguard/gui/package.html
new file mode 100644
index 0000000..4eedcc2
--- /dev/null
+++ b/src/proguard/gui/package.html
@@ -0,0 +1,3 @@
+<body>
+This package contains a GUI for ProGuard and ReTrace.
+</body>
diff --git a/src/proguard/gui/splash/BufferedSprite.java b/src/proguard/gui/splash/BufferedSprite.java
new file mode 100644
index 0000000..684f977
--- /dev/null
+++ b/src/proguard/gui/splash/BufferedSprite.java
@@ -0,0 +1,85 @@
+/* $Id: BufferedSprite.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+import java.awt.*;
+
+/**
+ * This Sprite encapsulates another Sprite, which is buffered on an Image.
+ *
+ * @author Eric Lafortune
+ */
+public class BufferedSprite implements Sprite
+{
+    private Image    bufferImage;
+    private Graphics bufferGraphics;
+    private Color    backgroundColor;
+    private Sprite   sprite;
+
+    private long cachedTime = -1;
+
+
+    /**
+     * Creates a new BufferedSprite.
+     * @param bufferImage     the Image that is used for the buffering.
+     * @param bufferGraphics  the Graphics of the Image.
+     * @param backgroundColor the background color that is used for the buffer.
+     * @param sprite          the Sprite that is painted in the buffer.
+     */
+    public BufferedSprite(Image    bufferImage,
+                          Graphics bufferGraphics,
+                          Color    backgroundColor,
+                          Sprite   sprite)
+    {
+
+        this.bufferImage     = bufferImage;
+        this.bufferGraphics  = bufferGraphics;
+        this.backgroundColor = backgroundColor;
+        this.sprite          = sprite;
+    }
+
+
+   // Implementation for Sprite.
+
+    public void paint(Graphics graphics, long time)
+    {
+        Rectangle clip = bufferGraphics.getClipBounds();
+
+        // Do we need to repaint the sprites in the buffer image?
+        if (time != cachedTime)
+        {
+            // Clear the background.
+            if (backgroundColor != null)
+            {
+                bufferGraphics.setColor(backgroundColor);
+                bufferGraphics.fillRect(0, 0, clip.width, clip.height);
+            }
+
+            // Draw the sprite.
+            sprite.paint(bufferGraphics, time);
+
+            cachedTime = time;
+        }
+
+        // Draw the buffer image.
+        graphics.drawImage(bufferImage, 0, 0, clip.width, clip.height, null);
+    }
+}
diff --git a/src/proguard/gui/splash/CircleSprite.java b/src/proguard/gui/splash/CircleSprite.java
new file mode 100644
index 0000000..21484df
--- /dev/null
+++ b/src/proguard/gui/splash/CircleSprite.java
@@ -0,0 +1,80 @@
+/* $Id: CircleSprite.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+import java.awt.Graphics;
+
+/**
+ * This Sprite represents an animated circle. It can optionally be filled.
+ *
+ * @author Eric Lafortune
+ */
+public class CircleSprite implements Sprite
+{
+    private boolean       filled;
+    private VariableColor color;
+    private VariableInt   x;
+    private VariableInt   y;
+    private VariableInt   radius;
+
+
+    /**
+     * Creates a new CircleSprite.
+     * @param filled specifies whether the rectangle should be filled.
+     * @param color  the variable color of the circle.
+     * @param x      the variable x-coordinate of the center of the circle.
+     * @param y      the variable y-coordinate of the center of the circle.
+     * @param radius the variable radius of the circle.
+     */
+    public CircleSprite(boolean       filled,
+                        VariableColor color,
+                        VariableInt   x,
+                        VariableInt   y,
+                        VariableInt   radius)
+    {
+        this.filled = filled;
+        this.color  = color;
+        this.x      = x;
+        this.y      = y;
+        this.radius = radius;
+    }
+
+
+    // Implementation for Sprite.
+
+    public void paint(Graphics graphics, long time)
+    {
+        graphics.setColor(color.getColor(time));
+
+        int xt = x.getInt(time);
+        int yt = y.getInt(time);
+        int r  = radius.getInt(time);
+
+        if (filled)
+        {
+            graphics.fillOval(xt - r, yt - r, 2 * r, 2 * r);
+        }
+        else
+        {
+            graphics.drawOval(xt - r, yt - r, 2 * r, 2 * r);
+        }
+    }
+}
diff --git a/src/proguard/gui/splash/ClipSprite.java b/src/proguard/gui/splash/ClipSprite.java
new file mode 100644
index 0000000..0cad063
--- /dev/null
+++ b/src/proguard/gui/splash/ClipSprite.java
@@ -0,0 +1,85 @@
+/* $Id: ClipSprite.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+import java.awt.*;
+
+/**
+ * This Sprite encapsulates another Sprite, which is clipped by a clip Sprite.
+ *
+ * @author Eric Lafortune
+ */
+public class ClipSprite implements Sprite
+{
+    private VariableColor insideClipColor;
+    private VariableColor outsideClipColor;
+    private Sprite        clipSprite;
+    private Sprite        sprite;
+
+
+    /**
+     * Creates a new ClipSprite.
+     * @param insideClipColor  the background color inside the clip sprite.
+     * @param outsideClipColor the background color outside the clip sprite.
+     * @param clipSprite       the clip Sprite.
+     * @param sprite           the clipped Sprite.
+     */
+    public ClipSprite(VariableColor insideClipColor,
+                      VariableColor outsideClipColor,
+                      Sprite        clipSprite,
+                      Sprite        sprite)
+    {
+        this.insideClipColor  = insideClipColor;
+        this.outsideClipColor = outsideClipColor;
+        this.clipSprite       = clipSprite;
+        this.sprite           = sprite;
+    }
+
+
+    // Implementation for Sprite.
+
+    public void paint(Graphics graphics, long time)
+    {
+        // Clear the background.
+        Color outsideColor = outsideClipColor.getColor(time);
+        Rectangle clip = graphics.getClipBounds();
+        graphics.setPaintMode();
+        graphics.setColor(outsideColor);
+        graphics.fillRect(0, 0, clip.width, clip.height);
+
+        // Draw the sprite in XOR mode.
+        OverrideGraphics2D g = new OverrideGraphics2D((Graphics2D)graphics);
+        Color insideColor = insideClipColor.getColor(time);
+        g.setOverrideXORMode(insideColor);
+        sprite.paint(g, time);
+        g.setOverrideXORMode(null);
+
+        // Clear the clip area.
+        g.setOverrideColor(insideColor);
+        clipSprite.paint(g, time);
+        g.setOverrideColor(null);
+
+        // Draw the sprite in XOR mode.
+        g.setOverrideXORMode(insideColor);
+        sprite.paint(g, time);
+        g.setOverrideXORMode(null);
+    }
+}
diff --git a/src/proguard/gui/splash/CompositeSprite.java b/src/proguard/gui/splash/CompositeSprite.java
new file mode 100644
index 0000000..8a27b35
--- /dev/null
+++ b/src/proguard/gui/splash/CompositeSprite.java
@@ -0,0 +1,56 @@
+/* $Id: CompositeSprite.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+import java.awt.Graphics;
+
+/**
+ * This Sprite is the composition of a list of Sprite objects.
+ *
+ * @author Eric Lafortune
+ */
+public class CompositeSprite implements Sprite
+{
+    private Sprite[] sprites;
+
+
+    /**
+     * Creates a new CompositeSprite.
+     * @param sprites the array of Sprite objects to which the painting will
+     *                be delegated, starting with the first element.
+     */
+    public CompositeSprite(Sprite[] sprites)
+    {
+        this.sprites = sprites;
+    }
+
+
+    // Implementation for Sprite.
+
+    public void paint(Graphics graphics, long time)
+    {
+        // Draw the sprites.
+        for (int index = 0; index < sprites.length; index++)
+        {
+            sprites[index].paint(graphics, time);
+        }
+    }
+}
diff --git a/src/proguard/gui/splash/ConstantColor.java b/src/proguard/gui/splash/ConstantColor.java
new file mode 100644
index 0000000..9391ff2
--- /dev/null
+++ b/src/proguard/gui/splash/ConstantColor.java
@@ -0,0 +1,51 @@
+/* $Id: ConstantColor.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+import java.awt.Color;
+
+/**
+ * This VariableColor is constant over time.
+ *
+ * @author Eric Lafortune
+ */
+public class ConstantColor implements VariableColor
+{
+    private Color value;
+
+
+    /**
+     * Creates a new ConstantColor.
+     * @param value the constant value.
+     */
+    public ConstantColor(Color value)
+    {
+        this.value = value;
+    }
+
+
+    // Implementation for VariableColor.
+
+    public Color getColor(long time)
+    {
+        return value;
+    }
+}
diff --git a/src/proguard/gui/splash/ConstantDouble.java b/src/proguard/gui/splash/ConstantDouble.java
new file mode 100644
index 0000000..da0e8fd
--- /dev/null
+++ b/src/proguard/gui/splash/ConstantDouble.java
@@ -0,0 +1,49 @@
+/* $Id: ConstantDouble.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+/**
+ * This VariableDouble is constant over time.
+ *
+ * @author Eric Lafortune
+ */
+public class ConstantDouble implements VariableDouble
+{
+    private double value;
+
+
+    /**
+     * Creates a new ConstantDouble.
+     * @param value the constant value.
+     */
+    public ConstantDouble(double value)
+    {
+        this.value = value;
+    }
+
+
+    // Implementation for VariableDouble.
+
+    public double getDouble(long time)
+    {
+        return value;
+    }
+}
diff --git a/src/proguard/gui/splash/ConstantFont.java b/src/proguard/gui/splash/ConstantFont.java
new file mode 100644
index 0000000..f69f2ad
--- /dev/null
+++ b/src/proguard/gui/splash/ConstantFont.java
@@ -0,0 +1,46 @@
+/* $Id: ConstantFont.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+import java.awt.Font;
+
+/**
+ * This VariableFont is constant over time.
+ *
+ * @author Eric Lafortune
+ */
+public class ConstantFont implements VariableFont
+{
+    private Font value;
+
+    public ConstantFont(Font value)
+    {
+        this.value = value;
+    }
+
+
+    // Implementation for VariableFont.
+
+    public Font getFont(long time)
+    {
+        return value;
+    }
+}
diff --git a/src/proguard/gui/splash/ConstantInt.java b/src/proguard/gui/splash/ConstantInt.java
new file mode 100644
index 0000000..c007256
--- /dev/null
+++ b/src/proguard/gui/splash/ConstantInt.java
@@ -0,0 +1,49 @@
+/* $Id: ConstantInt.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+/**
+ * This VariableInt is constant over time.
+ *
+ * @author Eric Lafortune
+ */
+public class ConstantInt implements VariableInt
+{
+    private int value;
+
+
+    /**
+     * Creates a new ConstantInt.
+     * @param value the constant value.
+     */
+    public ConstantInt(int value)
+    {
+        this.value = value;
+    }
+
+
+    // Implementation for VariableInt.
+
+    public int getInt(long time)
+    {
+        return value;
+    }
+}
diff --git a/src/proguard/gui/splash/ConstantString.java b/src/proguard/gui/splash/ConstantString.java
new file mode 100644
index 0000000..3983dcf
--- /dev/null
+++ b/src/proguard/gui/splash/ConstantString.java
@@ -0,0 +1,49 @@
+/* $Id: ConstantString.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+/**
+ * This VariableString is constant over time.
+ *
+ * @author Eric Lafortune
+ */
+public class ConstantString implements VariableString
+{
+    private String value;
+
+
+    /**
+     * Creates a new ConstantString.
+     * @param value the constant value.
+     */
+    public ConstantString(String value)
+    {
+        this.value = value;
+    }
+
+
+    // Implementation for VariableString.
+
+    public String getString(long time)
+    {
+        return value;
+    }
+}
diff --git a/src/proguard/gui/splash/ConstantTiming.java b/src/proguard/gui/splash/ConstantTiming.java
new file mode 100644
index 0000000..2c98fe4
--- /dev/null
+++ b/src/proguard/gui/splash/ConstantTiming.java
@@ -0,0 +1,58 @@
+/* $Id: ConstantTiming.java,v 1.7 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+/**
+ * This Timing is constant over time.
+ *
+ * @author Eric Lafortune
+ */
+public class ConstantTiming implements Timing
+{
+    private double timing;
+
+
+    /**
+     * Creates a new ConstantTiming with a value of 0.
+     * @param timing the constant value of the timing.
+     */
+    public ConstantTiming()
+    {
+        this(0.0);
+    }
+
+    /**
+     * Creates a new ConstantTiming with a given value.
+     * @param timing the constant value of the timing.
+     */
+    public ConstantTiming(double timing)
+    {
+        this.timing = timing;
+    }
+
+
+    // Implementation for Timing.
+
+    public double getTiming(long time)
+    {
+        return timing;
+    }
+}
diff --git a/src/proguard/gui/splash/ImageSprite.java b/src/proguard/gui/splash/ImageSprite.java
new file mode 100644
index 0000000..cf62703
--- /dev/null
+++ b/src/proguard/gui/splash/ImageSprite.java
@@ -0,0 +1,76 @@
+/* $Id: ImageSprite.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+import java.awt.*;
+
+/**
+ * This Sprite represents an animated image.
+ *
+ * @author Eric Lafortune
+ */
+public class ImageSprite implements Sprite
+{
+    private Image          image;
+    private VariableInt    x;
+    private VariableInt    y;
+    private VariableDouble scaleX;
+    private VariableDouble scaleY;
+
+
+    /**
+     * Creates a new ImageSprite.
+     * @param image  the Image to be painted.
+     * @param x      the variable x-coordinate of the upper-left corner of the image.
+     * @param y      the variable y-coordinate of the upper-left corner of the image.
+     * @param scaleX the variable x-scale of the image.
+     * @param scaleY the variable y-scale of the image.
+     */
+    public ImageSprite(Image          image,
+                       VariableInt    x,
+                       VariableInt    y,
+                       VariableDouble scaleX,
+                       VariableDouble scaleY)
+    {
+        this.image  = image;
+        this.x      = x;
+        this.y      = y;
+        this.scaleX = scaleX;
+        this.scaleY = scaleY;
+    }
+
+
+    // Implementation for Sprite.
+
+    public void paint(Graphics graphics, long time)
+    {
+        int xt = x.getInt(time);
+        int yt = y.getInt(time);
+
+        double scale_x = scaleX.getDouble(time);
+        double scale_y = scaleY.getDouble(time);
+
+        int width  = (int)(image.getWidth(null)  * scale_x);
+        int height = (int)(image.getHeight(null) * scale_y);
+
+        graphics.drawImage(image, xt, yt, width, height, null);
+    }
+}
diff --git a/src/proguard/gui/splash/LinearColor.java b/src/proguard/gui/splash/LinearColor.java
new file mode 100644
index 0000000..60c3666
--- /dev/null
+++ b/src/proguard/gui/splash/LinearColor.java
@@ -0,0 +1,72 @@
+/* $Id: LinearColor.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+import java.awt.Color;
+
+/**
+ * This VariableColor varies linearly with respect to its Timing.
+ *
+ * @author Eric Lafortune
+ */
+public class LinearColor implements VariableColor
+{
+    private Color  fromValue;
+    private Color  toValue;
+    private Timing timing;
+
+    private double cachedTiming = -1.0;
+    private Color  cachedColor;
+
+
+    /**
+     * Creates a new LinearColor.
+     * @param fromValue the value that corresponds to a timing of 0.
+     * @param toValue   the value that corresponds to a timing of 1.
+     * @param timing    the applied timing.
+     */
+    public LinearColor(Color fromValue, Color toValue, Timing timing)
+    {
+        this.fromValue = fromValue;
+        this.toValue   = toValue;
+        this.timing    = timing;
+    }
+
+
+    // Implementation for VariableColor.
+
+    public Color getColor(long time)
+    {
+        double t = timing.getTiming(time);
+        if (t != cachedTiming)
+        {
+            cachedTiming = t;
+            cachedColor =
+                t == 0.0 ? fromValue :
+                t == 1.0 ? toValue   :
+                           new Color((int)(fromValue.getRed()   + t * (toValue.getRed()   - fromValue.getRed())),
+                                     (int)(fromValue.getGreen() + t * (toValue.getGreen() - fromValue.getGreen())),
+                                     (int)(fromValue.getBlue()  + t * (toValue.getBlue()  - fromValue.getBlue())));
+        }
+
+        return cachedColor;
+    }
+}
diff --git a/src/proguard/gui/splash/LinearDouble.java b/src/proguard/gui/splash/LinearDouble.java
new file mode 100644
index 0000000..113a759
--- /dev/null
+++ b/src/proguard/gui/splash/LinearDouble.java
@@ -0,0 +1,55 @@
+/* $Id: LinearDouble.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+/**
+ * This VariableDouble varies linearly with respect to its Timing.
+ *
+ * @author Eric Lafortune
+ */
+public class LinearDouble implements VariableDouble
+{
+    private double fromValue;
+    private double toValue;
+    private Timing timing;
+
+
+    /**
+     * Creates a new LinearDouble.
+     * @param fromValue the value that corresponds to a timing of 0.
+     * @param toValue   the value that corresponds to a timing of 1.
+     * @param timing    the applied timing.
+     */
+    public LinearDouble(double fromValue, double toValue, Timing timing)
+    {
+        this.fromValue = fromValue;
+        this.toValue   = toValue;
+        this.timing    = timing;
+    }
+
+
+    // Implementation for VariableDouble.
+
+    public double getDouble(long time)
+    {
+        return fromValue + timing.getTiming(time) * (toValue - fromValue);
+    }
+}
diff --git a/src/proguard/gui/splash/LinearInt.java b/src/proguard/gui/splash/LinearInt.java
new file mode 100644
index 0000000..909fc90
--- /dev/null
+++ b/src/proguard/gui/splash/LinearInt.java
@@ -0,0 +1,55 @@
+/* $Id: LinearInt.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+/**
+ * This VariableColor varies linearly with respect to its Timing.
+ *
+ * @author Eric Lafortune
+ */
+public class LinearInt implements VariableInt
+{
+    private int    fromValue;
+    private int    toValue;
+    private Timing timing;
+
+
+    /**
+     * Creates a new LinearInt.
+     * @param fromValue the value that corresponds to a timing of 0.
+     * @param toValue   the value that corresponds to a timing of 1.
+     * @param timing    the applied timing.
+     */
+    public LinearInt(int fromValue, int toValue, Timing timing)
+    {
+        this.fromValue = fromValue;
+        this.toValue   = toValue;
+        this.timing    = timing;
+    }
+
+
+    // Implementation for VariableInt.
+
+    public int getInt(long time)
+    {
+        return (int) (fromValue + timing.getTiming(time) * (toValue - fromValue));
+    }
+}
diff --git a/src/proguard/gui/splash/LinearTiming.java b/src/proguard/gui/splash/LinearTiming.java
new file mode 100644
index 0000000..1dc3810
--- /dev/null
+++ b/src/proguard/gui/splash/LinearTiming.java
@@ -0,0 +1,55 @@
+/* $Id: LinearTiming.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+/**
+ * This Timing ramps up linearly from 0 to 1 in a given time interval.
+ *
+ * @author Eric Lafortune
+ */
+public class LinearTiming implements Timing
+{
+    private long fromTime;
+    private long toTime;
+
+
+    /**
+     * Creates a new LinearTiming.
+     * @param fromTime the time at which the timing starts ramping up from 0.
+     * @param toTime   the time at which the timing stops ramping up at 1.
+     */
+    public LinearTiming(long fromTime, long toTime)
+    {
+        this.fromTime = fromTime;
+        this.toTime   = toTime;
+    }
+
+
+    // Implementation for Timing.
+
+    public double getTiming(long time)
+    {
+        // Compute the clamped linear interpolation.
+        return time <= fromTime ? 0.0 :
+               time >= toTime   ? 1.0 :
+                                  (double)(time - fromTime) / (double)(toTime - fromTime);
+    }
+}
diff --git a/src/proguard/gui/splash/OverrideGraphics2D.java b/src/proguard/gui/splash/OverrideGraphics2D.java
new file mode 100644
index 0000000..4947bcd
--- /dev/null
+++ b/src/proguard/gui/splash/OverrideGraphics2D.java
@@ -0,0 +1,597 @@
+/* $Id: OverrideGraphics2D.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+import java.awt.*;
+import java.awt.RenderingHints.Key;
+import java.awt.font.*;
+import java.awt.geom.AffineTransform;
+import java.awt.image.*;
+import java.awt.image.renderable.RenderableImage;
+import java.text.AttributedCharacterIterator;
+import java.util.Map;
+
+/**
+ * This Graphics2D allows to fix some basic settings (Color, Font, Paint, Stroke,
+ * XORMode) of a delegate Graphics2D, overriding any subsequent attempts to
+ * change those settings.
+ *
+ * @author Eric Lafortune
+ */
+class OverrideGraphics2D extends Graphics2D
+{
+    private Graphics2D graphics;
+
+    private Color  overrideColor;
+    private Font   overrideFont;
+    private Paint  overridePaint;
+    private Stroke overrideStroke;
+    private Color  overrideXORMode;
+
+    private Color  color;
+    private Font   font;
+    private Paint  paint;
+    private Stroke stroke;
+
+
+    /**
+     * Creates a new OverrideGraphics2D.
+     * @param graphics the delegate Graphics2D.
+     */
+    public OverrideGraphics2D(Graphics2D graphics)
+    {
+        this.graphics = graphics;
+        this.color    = graphics.getColor();
+        this.font     = graphics.getFont();
+        this.paint    = graphics.getPaint();
+        this.stroke   = graphics.getStroke();
+    }
+
+
+    /**
+     * Fixes the Color of the Graphics2D.
+     *
+     * @param color the fixed Color, or <code>null</code> to undo the fixing.
+     */
+    public void setOverrideColor(Color color)
+    {
+        this.overrideColor = color;
+        graphics.setColor(color != null ? color : this.color);
+    }
+
+    /**
+     * Fixes the Font of the Graphics2D.
+     *
+     * @param font the fixed Font, or <code>null</code> to undo the fixing.
+     */
+    public void setOverrideFont(Font font)
+    {
+        this.overrideFont = font;
+        graphics.setFont(font != null ? font : this.font);
+    }
+
+    /**
+     * Fixes the Paint of the Graphics2D.
+     *
+     * @param paint the fixed Paint, or <code>null</code> to undo the fixing.
+     */
+    public void setOverridePaint(Paint paint)
+    {
+        this.overridePaint = paint;
+        graphics.setPaint(paint != null ? paint : this.paint);
+    }
+
+    /**
+     * Fixes the Stroke of the Graphics2D.
+     *
+     * @param stroke the fixed Stroke, or <code>null</code> to undo the fixing.
+     */
+    public void setOverrideStroke(Stroke stroke)
+    {
+        this.overrideStroke = stroke;
+        graphics.setStroke(stroke != null ? stroke : this.stroke);
+    }
+
+    /**
+     * Fixes the XORMode of the Graphics2D.
+     *
+     * @param color the fixed XORMode Color, or <code>null</code> to undo the fixing.
+     */
+    public void setOverrideXORMode(Color color)
+    {
+        this.overrideXORMode = color;
+        if (color != null)
+        {
+            graphics.setXORMode(color);
+        }
+        else
+        {
+            graphics.setPaintMode();
+        }
+    }
+
+
+    // Implementations for Graphics2D.
+
+    public void setColor(Color color)
+    {
+        this.color = color;
+        if (overrideColor == null)
+        {
+            graphics.setColor(color);
+        }
+    }
+
+    public void setFont(Font font)
+    {
+        this.font = font;
+        if (overrideFont == null)
+        {
+            graphics.setFont(font);
+        }
+    }
+
+    public void setPaint(Paint paint)
+    {
+        this.paint = paint;
+        if (overridePaint == null)
+        {
+            graphics.setPaint(paint);
+        }
+    }
+
+    public void setStroke(Stroke stroke)
+    {
+        this.stroke = stroke;
+        if (overrideStroke == null)
+        {
+            graphics.setStroke(stroke);
+        }
+    }
+
+    public void setXORMode(Color color)
+    {
+        if (overrideXORMode == null)
+        {
+            graphics.setXORMode(color);
+        }
+    }
+
+    public void setPaintMode()
+    {
+        if (overrideXORMode == null)
+        {
+            graphics.setPaintMode();
+        }
+    }
+
+
+    public Color getColor()
+    {
+        return overrideColor != null ? color : graphics.getColor();
+    }
+
+    public Font getFont()
+    {
+        return overrideFont != null ? font : graphics.getFont();
+    }
+
+    public Paint getPaint()
+    {
+        return overridePaint != null ? paint : graphics.getPaint();
+    }
+
+    public Stroke getStroke()
+    {
+        return overrideStroke != null ? stroke : graphics.getStroke();
+    }
+
+
+    public Graphics create()
+    {
+        OverrideGraphics2D g = new OverrideGraphics2D((Graphics2D)graphics.create());
+        g.setOverrideColor(overrideColor);
+        g.setOverrideFont(overrideFont);
+        g.setOverridePaint(overridePaint);
+        g.setOverrideStroke(overrideStroke);
+
+        return g;
+    }
+
+    public Graphics create(int x, int y, int width, int height)
+    {
+        OverrideGraphics2D g = new OverrideGraphics2D((Graphics2D)graphics.create(x, y, width, height));
+        g.setOverrideColor(overrideColor);
+        g.setOverrideFont(overrideFont);
+        g.setOverridePaint(overridePaint);
+        g.setOverrideStroke(overrideStroke);
+
+        return g;
+    }
+
+
+    // Delegation for Graphics2D
+
+    public void addRenderingHints(Map hints)
+    {
+        graphics.addRenderingHints(hints);
+    }
+
+    public void clearRect(int x, int y, int width, int height)
+    {
+        graphics.clearRect(x, y, width, height);
+    }
+
+    public void clip(Shape s)
+    {
+        graphics.clip(s);
+    }
+
+    public void clipRect(int x, int y, int width, int height)
+    {
+        graphics.clipRect(x, y, width, height);
+    }
+
+    public void copyArea(int x, int y, int width, int height, int dx, int dy)
+    {
+        graphics.copyArea(x, y, width, height, dx, dy);
+    }
+
+    public void dispose()
+    {
+        graphics.dispose();
+    }
+
+    public void draw(Shape s)
+    {
+        graphics.draw(s);
+    }
+
+    public void draw3DRect(int x, int y, int width, int height, boolean raised)
+    {
+        graphics.draw3DRect(x, y, width, height, raised);
+    }
+
+    public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle)
+    {
+        graphics.drawArc(x, y, width, height, startAngle, arcAngle);
+    }
+
+    public void drawBytes(byte[] data, int offset, int length, int x, int y)
+    {
+        graphics.drawBytes(data, offset, length, x, y);
+    }
+
+    public void drawChars(char[] data, int offset, int length, int x, int y)
+    {
+        graphics.drawChars(data, offset, length, x, y);
+    }
+
+    public void drawGlyphVector(GlyphVector g, float x, float y)
+    {
+        graphics.drawGlyphVector(g, x, y);
+    }
+
+    public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer)
+    {
+        return graphics.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer);
+    }
+
+    public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer)
+    {
+        return graphics.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer);
+    }
+
+    public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer)
+    {
+        return graphics.drawImage(img, x, y, width, height, bgcolor, observer);
+    }
+
+    public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)
+    {
+        return graphics.drawImage(img, x, y, width, height, observer);
+    }
+
+    public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer)
+    {
+        return graphics.drawImage(img, x, y, bgcolor, observer);
+    }
+
+    public boolean drawImage(Image img, int x, int y, ImageObserver observer)
+    {
+        return graphics.drawImage(img, x, y, observer);
+    }
+
+    public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs)
+    {
+        return graphics.drawImage(img, xform, obs);
+    }
+
+    public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y)
+    {
+        graphics.drawImage(img, op, x, y);
+    }
+
+    public void drawLine(int x1, int y1, int x2, int y2)
+    {
+        graphics.drawLine(x1, y1, x2, y2);
+    }
+
+    public void drawOval(int x, int y, int width, int height)
+    {
+        graphics.drawOval(x, y, width, height);
+    }
+
+    public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints)
+    {
+        graphics.drawPolygon(xPoints, yPoints, nPoints);
+    }
+
+    public void drawPolygon(Polygon p)
+    {
+        graphics.drawPolygon(p);
+    }
+
+    public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints)
+    {
+        graphics.drawPolyline(xPoints, yPoints, nPoints);
+    }
+
+    public void drawRect(int x, int y, int width, int height)
+    {
+        graphics.drawRect(x, y, width, height);
+    }
+
+    public void drawRenderableImage(RenderableImage img, AffineTransform xform)
+    {
+        graphics.drawRenderableImage(img, xform);
+    }
+
+    public void drawRenderedImage(RenderedImage img, AffineTransform xform)
+    {
+        graphics.drawRenderedImage(img, xform);
+    }
+
+    public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
+    {
+        graphics.drawRoundRect(x, y, width, height, arcWidth, arcHeight);
+    }
+
+    public void drawString(String s, float x, float y)
+    {
+        graphics.drawString(s, x, y);
+    }
+
+    public void drawString(String str, int x, int y)
+    {
+        graphics.drawString(str, x, y);
+    }
+
+    public void drawString(AttributedCharacterIterator iterator, float x, float y)
+    {
+        graphics.drawString(iterator, x, y);
+    }
+
+    public void drawString(AttributedCharacterIterator iterator, int x, int y)
+    {
+        graphics.drawString(iterator, x, y);
+    }
+
+    public boolean equals(Object obj)
+    {
+        return graphics.equals(obj);
+    }
+
+    public void fill(Shape s)
+    {
+        graphics.fill(s);
+    }
+
+    public void fill3DRect(int x, int y, int width, int height, boolean raised)
+    {
+        graphics.fill3DRect(x, y, width, height, raised);
+    }
+
+    public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle)
+    {
+        graphics.fillArc(x, y, width, height, startAngle, arcAngle);
+    }
+
+    public void fillOval(int x, int y, int width, int height)
+    {
+        graphics.fillOval(x, y, width, height);
+    }
+
+    public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints)
+    {
+        graphics.fillPolygon(xPoints, yPoints, nPoints);
+    }
+
+    public void fillPolygon(Polygon p)
+    {
+        graphics.fillPolygon(p);
+    }
+
+    public void fillRect(int x, int y, int width, int height)
+    {
+        graphics.fillRect(x, y, width, height);
+    }
+
+    public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
+    {
+        graphics.fillRoundRect(x, y, width, height, arcWidth, arcHeight);
+    }
+
+    public Color getBackground()
+    {
+        return graphics.getBackground();
+    }
+
+    public Shape getClip()
+    {
+        return graphics.getClip();
+    }
+
+    public Rectangle getClipBounds()
+    {
+        return graphics.getClipBounds();
+    }
+
+    public Rectangle getClipBounds(Rectangle r)
+    {
+        return graphics.getClipBounds(r);
+    }
+
+    public Rectangle getClipRect()
+    {
+        return graphics.getClipRect();
+    }
+
+    public Composite getComposite()
+    {
+        return graphics.getComposite();
+    }
+
+    public GraphicsConfiguration getDeviceConfiguration()
+    {
+        return graphics.getDeviceConfiguration();
+    }
+
+    public FontMetrics getFontMetrics()
+    {
+        return graphics.getFontMetrics();
+    }
+
+    public FontMetrics getFontMetrics(Font f)
+    {
+        return graphics.getFontMetrics(f);
+    }
+
+    public FontRenderContext getFontRenderContext()
+    {
+        return graphics.getFontRenderContext();
+    }
+
+    public Object getRenderingHint(Key hintKey)
+    {
+        return graphics.getRenderingHint(hintKey);
+    }
+
+    public RenderingHints getRenderingHints()
+    {
+        return graphics.getRenderingHints();
+    }
+
+    public AffineTransform getTransform()
+    {
+        return graphics.getTransform();
+    }
+
+    public int hashCode()
+    {
+        return graphics.hashCode();
+    }
+
+    public boolean hit(Rectangle rect, Shape s, boolean onStroke)
+    {
+        return graphics.hit(rect, s, onStroke);
+    }
+
+    public boolean hitClip(int x, int y, int width, int height)
+    {
+        return graphics.hitClip(x, y, width, height);
+    }
+
+    public void rotate(double theta)
+    {
+        graphics.rotate(theta);
+    }
+
+    public void rotate(double theta, double x, double y)
+    {
+        graphics.rotate(theta, x, y);
+    }
+
+    public void scale(double sx, double sy)
+    {
+        graphics.scale(sx, sy);
+    }
+
+    public void setBackground(Color color)
+    {
+        graphics.setBackground(color);
+    }
+
+    public void setClip(int x, int y, int width, int height)
+    {
+        graphics.setClip(x, y, width, height);
+    }
+
+    public void setClip(Shape clip)
+    {
+        graphics.setClip(clip);
+    }
+
+    public void setComposite(Composite comp)
+    {
+        graphics.setComposite(comp);
+    }
+
+    public void setRenderingHint(Key hintKey, Object hintValue)
+    {
+        graphics.setRenderingHint(hintKey, hintValue);
+    }
+
+    public void setRenderingHints(Map hints)
+    {
+        graphics.setRenderingHints(hints);
+    }
+
+    public void setTransform(AffineTransform Tx)
+    {
+        graphics.setTransform(Tx);
+    }
+
+    public void shear(double shx, double shy)
+    {
+        graphics.shear(shx, shy);
+    }
+
+    public String toString()
+    {
+        return graphics.toString();
+    }
+
+    public void transform(AffineTransform Tx)
+    {
+        graphics.transform(Tx);
+    }
+
+    public void translate(double tx, double ty)
+    {
+        graphics.translate(tx, ty);
+    }
+
+    public void translate(int x, int y)
+    {
+        graphics.translate(x, y);
+    }
+}
diff --git a/src/proguard/gui/splash/RectangleSprite.java b/src/proguard/gui/splash/RectangleSprite.java
new file mode 100644
index 0000000..146c08d
--- /dev/null
+++ b/src/proguard/gui/splash/RectangleSprite.java
@@ -0,0 +1,114 @@
+/* $Id: RectangleSprite.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+import java.awt.Graphics;
+
+/**
+ * This Sprite represents an animated rounded rectangle. It can optionally be filled.
+ *
+ * @author Eric Lafortune
+ */
+public class RectangleSprite implements Sprite
+{
+    private boolean       filled;
+    private VariableColor color;
+    private VariableInt   x;
+    private VariableInt   y;
+    private VariableInt   width;
+    private VariableInt   height;
+    private VariableInt   arcWidth;
+    private VariableInt   arcHeight;
+
+
+    /**
+     * Creates a new rectangular RectangleSprite.
+     * @param filled specifies whether the rectangle should be filled.
+     * @param color  the variable color of the rectangle.
+     * @param x      the variable x-coordinate of the upper-left corner of the rectangle.
+     * @param y      the variable y-coordinate of the upper-left corner of the rectangle.
+     * @param width  the variable width of the rectangle.
+     * @param height the variable height of the rectangle.
+     */
+    public RectangleSprite(boolean       filled,
+                           VariableColor color,
+                           VariableInt   x,
+                           VariableInt   y,
+                           VariableInt   width,
+                           VariableInt   height)
+    {
+        this(filled, color, x, y, width, height, new ConstantInt(0), new ConstantInt(0));
+    }
+
+
+    /**
+     * Creates a new RectangleSprite with rounded corners.
+     * @param filled specifies whether the rectangle should be filled.
+     * @param color      the variable color of the rectangle.
+     * @param x          the variable x-coordinate of the upper-left corner of the rectangle.
+     * @param y          the variable y-coordinate of the upper-left corner of the rectangle.
+     * @param width      the variable width of the rectangle.
+     * @param height     the variable height of the rectangle.
+     * @param arcwWidth  the variable width of the corner arcs.
+     * @param arcwHeight the variable height of the corner arcs.
+     */
+    public RectangleSprite(boolean       filled,
+                           VariableColor color,
+                           VariableInt   x,
+                           VariableInt   y,
+                           VariableInt   width,
+                           VariableInt   height,
+                           VariableInt   arcWidth,
+                           VariableInt   arcHeight)
+    {
+        this.filled    = filled;
+        this.color     = color;
+        this.x         = x;
+        this.y         = y;
+        this.width     = width;
+        this.height    = height;
+        this.arcWidth  = arcWidth;
+        this.arcHeight = arcHeight;
+    }
+
+    // Implementation for Sprite.
+
+    public void paint(Graphics graphics, long time)
+    {
+        graphics.setColor(color.getColor(time));
+
+        int xt = x.getInt(time);
+        int yt = y.getInt(time);
+        int w  = width.getInt(time);
+        int h  = height.getInt(time);
+        int aw = arcWidth.getInt(time);
+        int ah = arcHeight.getInt(time);
+
+        if (filled)
+        {
+            graphics.fillRoundRect(xt, yt, w, h, aw, ah);
+        }
+        else
+        {
+            graphics.drawRoundRect(xt, yt, w, h, aw, ah);
+        }
+    }
+}
diff --git a/src/proguard/gui/splash/SawToothTiming.java b/src/proguard/gui/splash/SawToothTiming.java
new file mode 100644
index 0000000..0c7c1ab
--- /dev/null
+++ b/src/proguard/gui/splash/SawToothTiming.java
@@ -0,0 +1,53 @@
+/* $Id: SawToothTiming.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+/**
+ * This Timing ramps up linearly from 0 to 1 in a given repeated time interval.
+ *
+ * @author Eric Lafortune
+ */
+public class SawToothTiming implements Timing
+{
+    private long period;
+    private long phase;
+
+
+    /**
+     * Creates a new SawToothTiming.
+     * @param period the time period for a full cycle.
+     * @param phase  the phase of the cycle, which is added to the actual time.
+     */
+    public SawToothTiming(long period, long phase)
+    {
+        this.period = period;
+        this.phase  = phase;
+    }
+
+
+    // Implementation for Timing.
+
+    public double getTiming(long time)
+    {
+        // Compute the translated and scaled saw-tooth function.
+        return (double)((time + phase) % period) / (double)period;
+    }
+}
diff --git a/src/proguard/gui/splash/ShadowedSprite.java b/src/proguard/gui/splash/ShadowedSprite.java
new file mode 100644
index 0000000..64639e6
--- /dev/null
+++ b/src/proguard/gui/splash/ShadowedSprite.java
@@ -0,0 +1,102 @@
+/* $Id: ShadowedSprite.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+import java.awt.*;
+
+/**
+ * This Sprite adds a drop shadow to another Sprite.
+ *
+ * @author Eric Lafortune
+ */
+public class ShadowedSprite implements Sprite
+{
+    private VariableInt    xOffset;
+    private VariableInt    yOffset;
+    private VariableDouble alpha;
+    private VariableInt    blur;
+    private Sprite         sprite;
+
+    private float cachedAlpha = -1.0f;
+    private Color cachedColor;
+
+
+    /**
+     * Creates a new ShadowedSprite.
+     * @param xOffset the variable x-offset of the shadow, relative to the sprite itself.
+     * @param yOffset the variable y-offset of the shadow, relative to the sprite itself.
+     * @param alpha   the variable darkness of the shadow (between 0 and 1).
+     * @param blur    the variable blur of the shadow (0 for sharp shadows, 1 or
+     *                more for increasingly blurry shadows).
+     * @param sprite  the Sprite to be painted with its shadow.
+     */
+    public ShadowedSprite(VariableInt    xOffset,
+                          VariableInt    yOffset,
+                          VariableDouble alpha,
+                          VariableInt    blur,
+                          Sprite         sprite)
+    {
+        this.xOffset = xOffset;
+        this.yOffset = yOffset;
+        this.alpha   = alpha;
+        this.blur    = blur;
+        this.sprite  = sprite;
+    }
+
+
+    // Implementation for Sprite.
+
+    public void paint(Graphics graphics, long time)
+    {
+        double l = alpha.getDouble(time);
+        int    b = blur.getInt(time) + 1;
+
+        // Set up the shadow graphics.
+        OverrideGraphics2D g = new OverrideGraphics2D((Graphics2D)graphics);
+
+        float a = 1.0f - (float)Math.pow(1.0 - l, 1.0/(b*b));
+        if (a != cachedAlpha)
+        {
+            cachedAlpha = a;
+            cachedColor = new Color(0f, 0f, 0f, a);
+        }
+        g.setOverrideColor(cachedColor);
+
+        int xo = xOffset.getInt(time) - b/2;
+        int yo = yOffset.getInt(time) - b/2;
+
+        // Draw the sprite's shadow in the shadow graphics.
+        for (int x = 0; x < b; x++)
+        {
+            for (int y = 0; y < b; y++)
+            {
+                int xt = xo + x;
+                int yt = yo + y;
+                g.translate(xt, yt);
+                sprite.paint(g, time);
+                g.translate(-xt, -yt);
+            }
+        }
+
+        // Draw the sprite itself in the ordinary graphics.
+        sprite.paint(graphics, time);
+    }
+}
diff --git a/src/proguard/gui/splash/SineTiming.java b/src/proguard/gui/splash/SineTiming.java
new file mode 100644
index 0000000..151ec73
--- /dev/null
+++ b/src/proguard/gui/splash/SineTiming.java
@@ -0,0 +1,53 @@
+/* $Id: SineTiming.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+/**
+ * This Timing varies between 0 and 1, as a sine wave over time.
+ *
+ * @author Eric Lafortune
+ */
+public class SineTiming implements Timing
+{
+    private long period;
+    private long phase;
+
+
+    /**
+     * Creates a new SineTiming.
+     * @param period the time period for a full cycle.
+     * @param phase  the phase of the cycle, which is added to the actual time.
+     */
+    public SineTiming(long period, long phase)
+    {
+        this.period = period;
+        this.phase  = phase;
+    }
+
+
+    // Implementation for Timing.
+
+    public double getTiming(long time)
+    {
+        // Compute the translated and scaled sine function.
+        return 0.5 + 0.5 * Math.sin(2.0 * Math.PI * (time + phase) / period);
+    }
+}
diff --git a/src/proguard/gui/splash/SmoothTiming.java b/src/proguard/gui/splash/SmoothTiming.java
new file mode 100644
index 0000000..89a3cd7
--- /dev/null
+++ b/src/proguard/gui/splash/SmoothTiming.java
@@ -0,0 +1,66 @@
+/* $Id: SmoothTiming.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+/**
+ * This Timing ramps up smoothly from 0 to 1 in a given time interval.
+ *
+ * @author Eric Lafortune
+ */
+public class SmoothTiming implements Timing
+{
+    private long fromTime;
+    private long toTime;
+
+
+    /**
+     * Creates a new SmoothTiming.
+     * @param fromTime the time at which the timing starts ramping up from 0.
+     * @param toTime   the time at which the timing stops ramping up at 1.
+     */
+    public SmoothTiming(long fromTime, long toTime)
+    {
+        this.fromTime = fromTime;
+        this.toTime   = toTime;
+    }
+
+
+    // Implementation for Timing.
+
+    public double getTiming(long time)
+    {
+        if (time <= fromTime)
+        {
+            return 0.0;
+        }
+
+        if (time >= toTime)
+        {
+            return 1.0;
+        }
+
+        // Compute the linear interpolation.
+        double timing = (double) (time - fromTime) / (double) (toTime - fromTime);
+
+        // Smooth the interpolation at the ends.
+        return timing * timing * (3.0 - 2.0 * timing);
+    }
+}
diff --git a/src/proguard/gui/splash/SplashPanel.java b/src/proguard/gui/splash/SplashPanel.java
new file mode 100644
index 0000000..6d30f8a
--- /dev/null
+++ b/src/proguard/gui/splash/SplashPanel.java
@@ -0,0 +1,217 @@
+/* $Id: SplashPanel.java,v 1.10 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+
+/**
+ * This JPanel renders an animated Sprite.
+ *
+ * @author Eric Lafortune
+ */
+public class SplashPanel extends JPanel
+{
+    private final MyAnimator  animator  = new MyAnimator();
+    private final MyRepainter repainter = new MyRepainter();
+
+    private Sprite sprite;
+    private double sleepFactor;
+
+    private long   startTime = Long.MAX_VALUE;
+    private long   stopTime;
+    private Thread animationThread;
+
+
+    /**
+     * Creates a new SplashPanel with the given Sprite, which will be animated
+     * indefinitely.
+     * @param sprite        the Sprite that will be animated.
+     * @param processorLoad the fraction of processing time to be spend on
+     *                      animating the Sprite (between 0 and 1).
+     */
+    public SplashPanel(Sprite sprite, double processorLoad)
+    {
+        this(sprite, processorLoad, (long)Integer.MAX_VALUE);
+    }
+
+
+    /**
+     * Creates a new SplashPanel with the given Sprite, which will be animated
+     * for a limited period of time.
+     * @param sprite        the Sprite that will be animated.
+     * @param processorLoad the fraction of processing time to be spend on
+     *                      animating the Sprite (between 0 and 1).
+     * @param stopTime      the number of milliseconds after which the
+     *                      animation will be stopped automatically.
+     */
+    public SplashPanel(Sprite sprite, double processorLoad, long stopTime)
+    {
+        this.sprite      = sprite;
+        this.sleepFactor = (1.0-processorLoad) / processorLoad;
+        this.stopTime    = stopTime;
+
+        // Restart the animation on a mouse click.
+        addMouseListener(new MouseAdapter()
+        {
+            public void mouseClicked(MouseEvent e)
+            {
+                SplashPanel.this.start();
+            }
+        });
+    }
+
+
+    /**
+     * Starts the animation.
+     */
+    public void start()
+    {
+        // Go to the beginning of the animation.
+        startTime = System.currentTimeMillis();
+
+        // Make sure we have an animation thread running.
+        if (animationThread == null)
+        {
+            animationThread = new Thread(animator);
+            animationThread.start();
+        }
+    }
+
+
+    /**
+     * Stops the animation.
+     */
+    public void stop()
+    {
+        // Go to the end of the animation.
+        startTime = 0L;
+
+        // Let the animation thread stop itself.
+        animationThread = null;
+
+        // Repaint the SplashPanel one last time.
+        try
+        {
+            SwingUtilities.invokeAndWait(repainter);
+        }
+        catch (Exception ex)
+        {
+        }
+    }
+
+
+    // Implementation for JPanel.
+
+    public void paintComponent(Graphics graphics)
+    {
+        super.paintComponent(graphics);
+
+        sprite.paint(graphics, System.currentTimeMillis() - startTime);
+    }
+
+
+    /**
+     * This Runnable makes sure its SplashPanel gets repainted regularly,
+     * depending on the targeted processor load.
+     */
+    private class MyAnimator implements Runnable
+    {
+        public void run()
+        {
+            try
+            {
+                while (animationThread != null)
+                {
+                    // Check if we should stop the animation.
+                    long time = System.currentTimeMillis();
+                    if (time > startTime + stopTime)
+                    {
+                        animationThread = null;
+                    }
+
+                    // Do a repaint and time it.
+                    SwingUtilities.invokeAndWait(repainter);
+                    time = System.currentTimeMillis() - time;
+
+                    // Sleep for a proportional while.
+                    Thread.sleep((long)(sleepFactor * time));
+                }
+            }
+            catch (Exception ex)
+            {
+            }
+        }
+    }
+
+
+    /**
+     * This Runnable repaints its SplashPanel.
+     */
+    private class MyRepainter implements Runnable
+    {
+        public void run()
+        {
+            SplashPanel.this.repaint();
+        }
+    }
+
+
+    /**
+     * A main method for testing the splash panel.
+     */
+    public static void main(String[] args)
+    {
+        JFrame frame = new JFrame();
+        frame.setTitle("Animation");
+        frame.setSize(800, 600);
+        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+        Dimension frameSize  = frame.getSize();
+        frame.setLocation((screenSize.width - frameSize.width)   / 2,
+                          (screenSize.height - frameSize.height) / 2);
+
+        Sprite sprite =
+            new ClipSprite(
+            new ConstantColor(Color.white),
+            new ConstantColor(Color.lightGray),
+            new CircleSprite(true,
+                             new ConstantColor(Color.green),
+                             new LinearInt(200, 600, new SineTiming(2345L, 0L)),
+                             new LinearInt(200, 400, new SineTiming(3210L, 0L)),
+                             new ConstantInt(150)),
+            new TextSprite(new ConstantString("ProGuard"),
+                           new ConstantFont(new Font("sansserif", Font.BOLD, 90)),
+                           new ConstantColor(Color.gray),
+                           new ConstantInt(200),
+                           new ConstantInt(300)));
+
+        SplashPanel panel = new SplashPanel(sprite, 0.5);
+        panel.setBackground(Color.white);
+
+        frame.getContentPane().add(panel);
+        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        frame.setVisible(true);
+
+        panel.start();
+    }
+}
diff --git a/src/proguard/gui/splash/Sprite.java b/src/proguard/gui/splash/Sprite.java
new file mode 100644
index 0000000..01e7fb2
--- /dev/null
+++ b/src/proguard/gui/splash/Sprite.java
@@ -0,0 +1,41 @@
+/* $Id: Sprite.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+import java.awt.Graphics;
+
+/**
+ * This interface describes objects that can paint themselves, possibly varying
+ * as a function of time.
+ *
+ * @author Eric Lafortune
+ */
+public interface Sprite
+{
+    /**
+     * Paints the object.
+     *
+     * @param graphics the Graphics to paint on.
+     * @param time     the time since the start of the animation, expressed in
+     *                  milliseconds.
+     */
+    public void paint(Graphics graphics, long time);
+}
diff --git a/src/proguard/gui/splash/TextSprite.java b/src/proguard/gui/splash/TextSprite.java
new file mode 100644
index 0000000..a31c9f6
--- /dev/null
+++ b/src/proguard/gui/splash/TextSprite.java
@@ -0,0 +1,102 @@
+/* $Id: TextSprite.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+import java.awt.*;
+
+/**
+ * This Sprite represents a text.
+ *
+ * @author Eric Lafortune
+ */
+public class TextSprite implements Sprite
+{
+    private VariableString[] text;
+    private VariableInt      spacing;
+    private VariableFont     font;
+    private VariableColor    color;
+    private VariableInt      x;
+    private VariableInt      y;
+
+
+    /**
+     * Creates a new TextSprite containing a single line of text.
+     * @param text  the variable text string.
+     * @param font  the variable text font.
+     * @param color the variable color.
+     * @param x     the variable x-coordinate of the lower-left corner of the text.
+     * @param y     the variable y-coordinate of the lower-left corner of the text.
+     */
+    public TextSprite(VariableString text,
+                      VariableFont   font,
+                      VariableColor  color,
+                      VariableInt    x,
+                      VariableInt    y)
+    {
+        this(new VariableString[] { text }, new ConstantInt(0), font, color, x, y);
+    }
+
+
+    /**
+     * Creates a new TextSprite containing a multiple lines of text.
+     * @param text    the variable text strings.
+     * @param spacing the variable spacing between the lines of text.
+     * @param font    the variable text font.
+     * @param color   the variable color.
+     * @param x       the variable x-coordinate of the lower-left corner of the
+     *                first line of text.
+     * @param y       the variable y-coordinate of the lower-left corner of the
+     *                first line of text.
+     */
+    public TextSprite(VariableString[] text,
+                      VariableInt      spacing,
+                      VariableFont     font,
+                      VariableColor    color,
+                      VariableInt      x,
+                      VariableInt      y)
+    {
+
+        this.text    = text;
+        this.spacing = spacing;
+        this.font    = font;
+        this.color   = color;
+        this.x       = x;
+        this.y       = y;
+    }
+
+
+    // Implementation for Sprite.
+
+    public void paint(Graphics graphics, long time)
+    {
+
+        int xt = x.getInt(time);
+        int yt = y.getInt(time);
+
+        graphics.setFont(font.getFont(time));
+        graphics.setColor(color.getColor(time));
+
+        for (int index = 0; index < text.length; index++)
+        {
+            graphics.drawString(text[index].getString(time), xt, yt + index * spacing.getInt(time));
+        }
+    }
+}
diff --git a/src/proguard/gui/splash/TimeSwitchSprite.java b/src/proguard/gui/splash/TimeSwitchSprite.java
new file mode 100644
index 0000000..53bd207
--- /dev/null
+++ b/src/proguard/gui/splash/TimeSwitchSprite.java
@@ -0,0 +1,75 @@
+/* $Id: TimeSwitchSprite.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+import java.awt.Graphics;
+
+/**
+ * This Sprite displays another Sprite in a given time interval.
+ * The time of the encapsulated Sprite is shifted by the start time.
+ *
+ * @author Eric Lafortune
+ */
+public class TimeSwitchSprite implements Sprite
+{
+    private long   onTime;
+    private long   offtime;
+    private Sprite sprite;
+
+
+    /**
+     * Creates a new TimeSwitchSprite for displaying a given Sprite starting at a
+     * given time.
+     * @param onTime the start time.
+     * @param sprite the toggled Sprite.
+     */
+    public TimeSwitchSprite(long onTime, Sprite sprite)
+    {
+        this(onTime, 0L, sprite);
+    }
+
+
+    /**
+     * Creates a new TimeSwitchSprite for displaying a given Sprite  in a given
+     * time interval.
+     * @param onTime the start time.
+     * @param offTime the stop time.
+     * @param sprite the toggled Sprite.
+     */
+    public TimeSwitchSprite(long onTime, long offtime, Sprite sprite)
+    {
+        this.onTime  = onTime;
+        this.offtime = offtime;
+        this.sprite  = sprite;
+    }
+
+
+    // Implementation for Sprite.
+
+    public void paint(Graphics graphics, long time)
+    {
+        if (time >= onTime && (offtime <= 0 || time <= offtime))
+        {
+            sprite.paint(graphics, time - onTime);
+        }
+
+    }
+}
diff --git a/src/proguard/gui/splash/Timing.java b/src/proguard/gui/splash/Timing.java
new file mode 100644
index 0000000..e9b0029
--- /dev/null
+++ b/src/proguard/gui/splash/Timing.java
@@ -0,0 +1,34 @@
+/* $Id: Timing.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+/**
+ * This interface maps a time to a normalized timing between 0 and 1.
+ *
+ * @author Eric Lafortune
+ */
+interface Timing
+{
+    /**
+     * Returns the timing for the given time.
+     */
+    public double getTiming(long time);
+}
diff --git a/src/proguard/gui/splash/TypeWriterString.java b/src/proguard/gui/splash/TypeWriterString.java
new file mode 100644
index 0000000..6c1fd27
--- /dev/null
+++ b/src/proguard/gui/splash/TypeWriterString.java
@@ -0,0 +1,71 @@
+/* $Id: TypeWriterString.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+/**
+ * This VariableString produces a String that grows linearly with respect to its
+ * Timing, as if it is being written on a typewriter. A cursor at the end
+ * precedes the typed characters.
+ *
+ * @author Eric Lafortune
+ */
+public class TypeWriterString implements VariableString
+{
+    private String string;
+    private Timing timing;
+
+    private int    cachedLength = -1;
+    private String cachedString;
+
+
+    /**
+     * Creates a new TypeWriterString.
+     * @param string the basic String.
+     * @param timing the applied timing.
+     */
+    public TypeWriterString(String string, Timing timing)
+    {
+        this.string = string;
+        this.timing = timing;
+    }
+
+
+    // Implementation for VariableString.
+
+    public String getString(long time)
+    {
+        double t = timing.getTiming(time);
+
+        int stringLength = string.length();
+        int length = (int)(stringLength * t + 0.5);
+        if (length != cachedLength)
+        {
+            cachedLength = length;
+            cachedString = string.substring(0, length);
+            if (t > 0.0 && length < stringLength)
+            {
+                cachedString += "_";
+            }
+        }
+
+        return cachedString;
+    }
+}
diff --git a/src/proguard/gui/splash/VariableColor.java b/src/proguard/gui/splash/VariableColor.java
new file mode 100644
index 0000000..715d353
--- /dev/null
+++ b/src/proguard/gui/splash/VariableColor.java
@@ -0,0 +1,36 @@
+/* $Id: VariableColor.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+import java.awt.Color;
+
+/**
+ * This interface represents a Color that varies with time.
+ *
+ * @author Eric Lafortune
+ */
+interface VariableColor
+{
+    /**
+     * Returns the Color for the given time.
+     */
+    public Color getColor(long time);
+}
diff --git a/src/proguard/gui/splash/VariableDouble.java b/src/proguard/gui/splash/VariableDouble.java
new file mode 100644
index 0000000..78dab89
--- /dev/null
+++ b/src/proguard/gui/splash/VariableDouble.java
@@ -0,0 +1,34 @@
+/* $Id: VariableDouble.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+/**
+ * This interface represents a double that varies with time.
+ *
+ * @author Eric Lafortune
+ */
+interface VariableDouble
+{
+    /**
+     * Returns the double for the given time.
+     */
+    public double getDouble(long time);
+}
diff --git a/src/proguard/gui/splash/VariableFont.java b/src/proguard/gui/splash/VariableFont.java
new file mode 100644
index 0000000..9da8e73
--- /dev/null
+++ b/src/proguard/gui/splash/VariableFont.java
@@ -0,0 +1,36 @@
+/* $Id: VariableFont.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+import java.awt.*;
+
+/**
+ * This interface represents a Font that varies with time.
+ *
+ * @author Eric Lafortune
+ */
+interface VariableFont
+{
+    /**
+     * Returns the Font for the given time.
+     */
+    public Font getFont(long time);
+}
diff --git a/src/proguard/gui/splash/VariableInt.java b/src/proguard/gui/splash/VariableInt.java
new file mode 100644
index 0000000..53f583d
--- /dev/null
+++ b/src/proguard/gui/splash/VariableInt.java
@@ -0,0 +1,34 @@
+/* $Id: VariableInt.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+/**
+ * This interface represents an integer that varies with time.
+ *
+ * @author Eric Lafortune
+ */
+interface VariableInt
+{
+    /**
+     * Returns the integer for the given time.
+     */
+    public int getInt(long time);
+}
diff --git a/src/proguard/gui/splash/VariableSizeFont.java b/src/proguard/gui/splash/VariableSizeFont.java
new file mode 100644
index 0000000..db0d9d5
--- /dev/null
+++ b/src/proguard/gui/splash/VariableSizeFont.java
@@ -0,0 +1,65 @@
+/* $Id: VariableSizeFont.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+import java.awt.Font;
+
+/**
+ * This VariableFont varies in size with respect to its Timing.
+ *
+ * @author Eric Lafortune
+ */
+public class VariableSizeFont implements VariableFont
+{
+    private Font           font;
+    private VariableDouble size;
+
+    private float cachedSize = -1.0f;
+    private Font  cachedFont;
+
+
+    /**
+     * Creates a new VariableSizeFont
+     * @param font the base font.
+     * @param size the variable size of the font.
+     */
+    public VariableSizeFont(Font font, VariableDouble size)
+    {
+        this.font = font;
+        this.size = size;
+    }
+
+
+    // Implementation for VariableFont.
+
+    public Font getFont(long time)
+    {
+        float s = (float)size.getDouble(time);
+
+        if (s != cachedSize)
+        {
+            cachedSize = s;
+            cachedFont = font.deriveFont((float)s);
+        }
+
+        return cachedFont;
+    }
+}
diff --git a/src/proguard/gui/splash/VariableString.java b/src/proguard/gui/splash/VariableString.java
new file mode 100644
index 0000000..63e9e03
--- /dev/null
+++ b/src/proguard/gui/splash/VariableString.java
@@ -0,0 +1,34 @@
+/* $Id: VariableString.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui.splash;
+
+/**
+ * This interface represents a String that varies with time.
+ *
+ * @author Eric Lafortune
+ */
+interface VariableString
+{
+    /**
+     * Returns the String for the given time.
+     */
+    public String getString(long time);
+}
diff --git a/src/proguard/gui/splash/package.html b/src/proguard/gui/splash/package.html
new file mode 100644
index 0000000..209fad7
--- /dev/null
+++ b/src/proguard/gui/splash/package.html
@@ -0,0 +1,4 @@
+<body>
+This package contains a library for creating splash screens and animations
+with text, graphical elements, and some special effects.
+</body>
diff --git a/src/proguard/gui/vtitle.gif b/src/proguard/gui/vtitle.gif
new file mode 100644
index 0000000..646c69b
Binary files /dev/null and b/src/proguard/gui/vtitle.gif differ
diff --git a/src/proguard/io/CascadingDataEntryWriter.java b/src/proguard/io/CascadingDataEntryWriter.java
new file mode 100644
index 0000000..f967ec4
--- /dev/null
+++ b/src/proguard/io/CascadingDataEntryWriter.java
@@ -0,0 +1,86 @@
+/* $Id: CascadingDataEntryWriter.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import proguard.util.*;
+
+import java.io.*;
+
+/**
+ * This DataEntryWriter delegates to a given DataEntryWriter, or failing that,
+ * to another given DataEntryWriter.
+ *
+ * @author Eric Lafortune
+ */
+public class CascadingDataEntryWriter implements DataEntryWriter
+{
+    private DataEntryWriter dataEntryWriter1;
+    private DataEntryWriter dataEntryWriter2;
+
+
+    /**
+     * Creates a new FilteredDataEntryWriter.
+     * @param dataEntryWriter1 the DataEntryWriter to which the writing will be
+     *                         delegated first.
+     * @param dataEntryWriter2 the DataEntryWriter to which the writing will be
+     *                         delegated, if the first one can't provide an
+     *                         output stream.
+     */
+    public CascadingDataEntryWriter(DataEntryWriter dataEntryWriter1,
+                                    DataEntryWriter dataEntryWriter2)
+    {
+        this.dataEntryWriter1 = dataEntryWriter1;
+        this.dataEntryWriter2 = dataEntryWriter2;
+    }
+
+
+    // Implementations for DataEntryWriter.
+
+    public OutputStream getOutputStream(DataEntry dataEntry) throws IOException
+    {
+        return getOutputStream(dataEntry,  null);
+    }
+
+
+    public OutputStream getOutputStream(DataEntry dataEntry,
+                                        Finisher  finisher) throws IOException
+    {
+        // Try to get an output stream from the first data entry writer.
+        OutputStream outputStream =
+            dataEntryWriter1.getOutputStream(dataEntry, finisher);
+
+        // Return it, if it's not null. Otherwise try to get an output stream
+        // from the second data entry writer.
+        return outputStream != null ?
+            outputStream :
+            dataEntryWriter2.getOutputStream(dataEntry, finisher);
+    }
+
+
+    public void close() throws IOException
+    {
+        dataEntryWriter1.close();
+        dataEntryWriter2.close();
+
+        dataEntryWriter1 = null;
+        dataEntryWriter2 = null;
+    }
+}
diff --git a/src/proguard/io/ClassFileFilter.java b/src/proguard/io/ClassFileFilter.java
new file mode 100644
index 0000000..b439b52
--- /dev/null
+++ b/src/proguard/io/ClassFileFilter.java
@@ -0,0 +1,72 @@
+/* $Id: ClassFileFilter.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import proguard.classfile.*;
+import proguard.util.*;
+
+import java.io.*;
+
+
+/**
+ * This DataEntryReader delegates to one of two other DataEntryReader instances,
+ * depending on the extension of the data entry.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassFileFilter implements DataEntryReader
+{
+    private FilteredDataEntryReader filteredDataEntryReader;
+
+
+    /**
+     * Creates a new ClassFileFilter that delegates reading class files to the
+     * given reader.
+     */
+    public ClassFileFilter(DataEntryReader classFileReader)
+    {
+        this(classFileReader, null);
+    }
+
+
+    /**
+     * Creates a new ClassFileFilter that delegates to either of the two given
+     * readers.
+     */
+    public ClassFileFilter(DataEntryReader classFileReader,
+                           DataEntryReader dataEntryReader)
+    {
+        filteredDataEntryReader =
+            new FilteredDataEntryReader(
+            new DataEntryNameFilter(
+            new ExtensionMatcher(ClassConstants.CLASS_FILE_EXTENSION)),
+            classFileReader,
+            dataEntryReader);
+    }
+
+
+    // Implementations for DataEntryReader.
+
+    public void read(DataEntry dataEntry) throws IOException
+    {
+        filteredDataEntryReader.read(dataEntry);
+    }
+}
diff --git a/src/proguard/io/ClassFileReader.java b/src/proguard/io/ClassFileReader.java
new file mode 100644
index 0000000..75b33ea
--- /dev/null
+++ b/src/proguard/io/ClassFileReader.java
@@ -0,0 +1,94 @@
+/* $Id: ClassFileReader.java,v 1.3 2004/11/20 15:08:57 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+
+/**
+ * This DataEntryReader applies a given ClassFileVisitor to the class file
+ * definitions that it reads.
+ * <p>
+ * Class files are read as ProgramClassFile objects or LibraryClassFile objects,
+ * depending on the <code>isLibrary</code> flag.
+ * <p>
+ * In case of libraries, only public class files are considered, if the
+ * <code>skipNonPublicLibraryClasses</code> flag is set.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassFileReader implements DataEntryReader
+{
+    private boolean          isLibrary;
+    private boolean          skipNonPublicLibraryClasses;
+    private boolean          skipNonPublicLibraryClassMembers;
+    private ClassFileVisitor classFileVisitor;
+
+
+    /**
+     * Creates a new DataEntryClassFileFilter for reading the specified
+     * ClassFile objects.
+     */
+    public ClassFileReader(boolean          isLibrary,
+                           boolean          skipNonPublicLibraryClasses,
+                           boolean          skipNonPublicLibraryClassMembers,
+                           ClassFileVisitor classFileVisitor)
+    {
+        this.isLibrary                        = isLibrary;
+        this.skipNonPublicLibraryClasses      = skipNonPublicLibraryClasses;
+        this.skipNonPublicLibraryClassMembers = skipNonPublicLibraryClassMembers;
+        this.classFileVisitor                 = classFileVisitor;
+    }
+
+
+    // Implementations for DataEntryReader.
+
+    public void read(DataEntry dataEntry) throws IOException
+    {
+        try
+        {
+            // Get the input stream.
+            InputStream inputStream = dataEntry.getInputStream();
+
+            // Wrap it into a data input stream.
+            DataInputStream dataInputStream = new DataInputStream(inputStream);
+
+            // Create a ClassFile representation.
+            ClassFile classFile = isLibrary ?
+                (ClassFile)LibraryClassFile.create(dataInputStream, skipNonPublicLibraryClasses, skipNonPublicLibraryClassMembers) :
+                (ClassFile)ProgramClassFile.create(dataInputStream);
+
+            // Apply the visitor.
+            if (classFile != null)
+            {
+                classFile.accept(classFileVisitor);
+            }
+
+            dataEntry.closeInputStream();
+        }
+        catch (Exception ex)
+        {
+            throw new IOException("Can't process class file ["+dataEntry.getName()+"] ("+ex.getMessage()+")");
+        }
+    }
+}
diff --git a/src/proguard/io/ClassFileRewriter.java b/src/proguard/io/ClassFileRewriter.java
new file mode 100644
index 0000000..e50f506
--- /dev/null
+++ b/src/proguard/io/ClassFileRewriter.java
@@ -0,0 +1,77 @@
+/* $Id: ClassFileRewriter.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import proguard.classfile.*;
+
+import java.io.*;
+
+
+/**
+ * This DataEntryReader reads class file entries and writes their corresponding
+ * versions from the ClassPool to a given DataEntryWriter.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassFileRewriter implements DataEntryReader
+{
+    private ClassPool       classPool;
+    private DataEntryWriter dataEntryWriter;
+
+
+    public ClassFileRewriter(ClassPool       classPool,
+                             DataEntryWriter dataEntryWriter)
+    {
+        this.classPool       = classPool;
+        this.dataEntryWriter = dataEntryWriter;
+    }
+
+
+    // Implementations for DataEntryReader.
+
+    public void read(DataEntry dataEntry) throws IOException
+    {
+        String inputName = dataEntry.getName();
+        String className = inputName.substring(0, inputName.length() - ClassConstants.CLASS_FILE_EXTENSION.length());
+
+        // Find the modified class corrsponding to the input entry.
+        ProgramClassFile programClassFile = (ProgramClassFile)classPool.getClass(className);
+        if (programClassFile != null)
+        {
+            // Rename the data entry if necessary.
+            String newClassName = programClassFile.getName();
+            if (!className.equals(newClassName))
+            {
+                dataEntry = new RenamedDataEntry(dataEntry, newClassName + ClassConstants.CLASS_FILE_EXTENSION);
+            }
+
+            // Get the output entry corresponding to this input entry.
+            OutputStream outputStream = dataEntryWriter.getOutputStream(dataEntry);
+            if (outputStream != null)
+            {
+                // Write the class to the output entry.
+                DataOutputStream classOutputStream = new DataOutputStream(outputStream);
+                programClassFile.write(classOutputStream);
+                classOutputStream.flush();
+            }
+        }
+    }
+}
diff --git a/src/proguard/io/DataEntry.java b/src/proguard/io/DataEntry.java
new file mode 100644
index 0000000..03aeb9d
--- /dev/null
+++ b/src/proguard/io/DataEntry.java
@@ -0,0 +1,55 @@
+/* $Id: DataEntry.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import java.io.*;
+
+/**
+ * This interface describes a data entry, e.g. a ZIP entry or a file.
+ *
+ * @author Eric Lafortune
+ */
+public interface DataEntry
+{
+    /**
+     * Returns the name of this data entry.
+     */
+    public String getName();
+
+
+    /**
+     * Returns an input stream for reading the content of this data entry.
+     */
+    public InputStream getInputStream() throws IOException;
+
+
+    /**
+     * Closes the previously retrieved InputStream.
+     */
+    public void closeInputStream() throws IOException;
+
+
+    /**
+     * Returns the parent of this data entry, or <code>null</null> if it doesn't
+     * have one.
+     */
+    public DataEntry getParent();
+}
diff --git a/src/proguard/io/DataEntryCopier.java b/src/proguard/io/DataEntryCopier.java
new file mode 100644
index 0000000..23f0e5b
--- /dev/null
+++ b/src/proguard/io/DataEntryCopier.java
@@ -0,0 +1,243 @@
+/* $Id: DataEntryCopier.java,v 1.3 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import proguard.util.*;
+
+import java.io.*;
+
+
+/**
+ * This DataEntryReader writes the ZIP entries and files that it reads to a
+ * given DataEntryWriter.
+ *
+ * @author Eric Lafortune
+ */
+public class DataEntryCopier implements DataEntryReader
+{
+    private static final int BUFFER_SIZE = 1024;
+
+    private DataEntryWriter dataEntryWriter;
+    private byte[]          buffer = new byte[BUFFER_SIZE];
+
+
+
+    public DataEntryCopier(DataEntryWriter dataEntryWriter)
+    {
+        this.dataEntryWriter = dataEntryWriter;
+    }
+
+
+    // Implementations for DataEntryReader.
+
+    public void read(DataEntry dataEntry) throws IOException
+    {
+        try
+        {
+
+            // Get the output entry corresponding to this input entry.
+            OutputStream outputStream = dataEntryWriter.getOutputStream(dataEntry);
+            if (outputStream != null)
+            {
+                InputStream inputStream = dataEntry.getInputStream();
+
+                // Copy the data from the input entry to the output entry.
+                copyData(inputStream, outputStream);
+
+                // Close the data entries.
+                dataEntry.closeInputStream();
+            }
+        }
+        catch (IOException ex)
+        {
+            System.err.println("Warning: can't write resource [" + dataEntry.getName() + "] (" + ex.getMessage() + ")");
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Copies all data that it can read from the given input stream to the
+     * given output stream.
+     */
+    private void copyData(InputStream  inputStream,
+                          OutputStream outputStream)
+    throws IOException
+    {
+        while (true)
+        {
+            int count = inputStream.read(buffer);
+            if (count < 0)
+            {
+                break;
+            }
+            outputStream.write(buffer, 0, count);
+        }
+
+        outputStream.flush();
+    }
+
+
+    /**
+     * A main method for testing file/jar/war/directory copying.
+     */
+    public static void main(String[] args)
+    {
+        try
+        {
+            String input  = args[0];
+            String output = args[1];
+
+            boolean outputIsJar = output.endsWith(".jar");
+            boolean outputIsWar = output.endsWith(".war");
+            boolean outputIsEar = output.endsWith(".ear");
+            boolean outputIsZip = output.endsWith(".zip");
+
+            DataEntryWriter writer = new DirectoryWriter(new File(output),
+                                                         outputIsJar ||
+                                                         outputIsWar ||
+                                                         outputIsEar ||
+                                                         outputIsZip);
+
+        if (!outputIsJar)
+        {
+            // Zip up any zips, if necessary.
+            DataEntryWriter zipWriter = new JarWriter(writer);
+            if (outputIsZip)
+            {
+                // Always zip.
+                writer = zipWriter;
+            }
+            else
+            {
+                // Only zip up zips.
+                writer = new FilteredDataEntryWriter(new DataEntryParentFilter(
+                                                     new DataEntryNameFilter(
+                                                     new ExtensionMatcher(".zip"))),
+                                                     zipWriter,
+                                                     writer);
+            }
+
+            // Zip up any wars, if necessary.
+            DataEntryWriter warWriter = new JarWriter(writer);
+            if (outputIsWar)
+            {
+                // Always zip.
+                writer = warWriter;
+            }
+            else
+            {
+                // Only zip up wars.
+                writer = new FilteredDataEntryWriter(new DataEntryParentFilter(
+                                                     new DataEntryNameFilter(
+                                                     new ExtensionMatcher(".war"))),
+                                                     warWriter,
+                                                     writer);
+            }
+        }
+
+        // Zip up any jars, if necessary.
+        DataEntryWriter jarWriter = new JarWriter(writer);
+        if (outputIsJar)
+        {
+            // Always zip.
+            writer = jarWriter;
+        }
+        else
+        {
+            // Only zip up jars.
+            writer = new FilteredDataEntryWriter(new DataEntryParentFilter(
+                                                 new DataEntryNameFilter(
+                                                 new ExtensionMatcher(".jar"))),
+                                                 jarWriter,
+                                                 writer);
+        }
+
+
+            // Create the copying DataEntryReader.
+            DataEntryReader reader = new DataEntryCopier(writer);
+
+
+            boolean inputIsJar = input.endsWith(".jar");
+            boolean inputIsWar = input.endsWith(".war");
+            boolean inputIsZip = input.endsWith(".zip");
+
+            // Unzip any jars, if necessary.
+            DataEntryReader jarReader = new JarReader(reader);
+            if (inputIsJar)
+            {
+                // Always unzip.
+                reader = jarReader;
+            }
+            else
+            {
+                // Only unzip jar entries.
+                reader = new FilteredDataEntryReader(new DataEntryNameFilter(
+                                                     new ExtensionMatcher(".jar")),
+                                                     jarReader,
+                                                     reader);
+
+                // Unzip any wars, if necessary.
+                DataEntryReader warReader = new JarReader(reader);
+                if (inputIsWar)
+                {
+                    // Always unzip.
+                    reader = warReader;
+                }
+                else
+                {
+                    // Only unzip war entries.
+                    reader = new FilteredDataEntryReader(new DataEntryNameFilter(
+                                                         new ExtensionMatcher(".war")),
+                                                         warReader,
+                                                         reader);
+                }
+
+                // Unzip any zips, if necessary.
+                DataEntryReader zipReader = new JarReader(reader);
+                if (inputIsZip)
+                {
+                    // Always unzip.
+                    reader = zipReader;
+                }
+                else
+                {
+                    // Only unzip zip entries.
+                    reader = new FilteredDataEntryReader(new DataEntryNameFilter(
+                                                         new ExtensionMatcher(".zip")),
+                                                         zipReader,
+                                                         reader);
+                }
+            }
+
+            DirectoryPump directoryReader = new DirectoryPump(new File(input));
+
+            directoryReader.pumpDataEntries(reader);
+
+            writer.close();
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/src/proguard/io/DataEntryFilter.java b/src/proguard/io/DataEntryFilter.java
new file mode 100644
index 0000000..e2110e4
--- /dev/null
+++ b/src/proguard/io/DataEntryFilter.java
@@ -0,0 +1,38 @@
+/* $Id: DataEntryFilter.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+
+/**
+ * This interface provides a method to filter data entries.
+ *
+ * @author Eric Lafortune
+ */
+public interface DataEntryFilter
+{
+    /**
+     * Checks whether the filter accepts the given data entry.
+     * @param dataEntry the data entry to filter.
+     * @return a boolean indicating whether the filter accepts the given data
+     *         entry.
+     */
+    public boolean accepts(DataEntry dataEntry);
+}
diff --git a/src/proguard/io/DataEntryNameFilter.java b/src/proguard/io/DataEntryNameFilter.java
new file mode 100644
index 0000000..fd25b46
--- /dev/null
+++ b/src/proguard/io/DataEntryNameFilter.java
@@ -0,0 +1,54 @@
+/* $Id: DataEntryNameFilter.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import proguard.util.StringMatcher;
+
+/**
+ * This DataEntryFilter filters data entries based on whether their names match
+ * a given StringMatcher.
+ *
+ * @author Eric Lafortune
+ */
+public class DataEntryNameFilter
+implements   DataEntryFilter
+{
+    private StringMatcher stringMatcher;
+
+
+    /**
+     * Creates a new DataEntryNameFilter.
+     * @param stringMatcher the string matcher that will be applied to the names
+     *                      of the filtered data entries.
+     */
+    public DataEntryNameFilter(StringMatcher stringMatcher)
+    {
+        this.stringMatcher = stringMatcher;
+    }
+
+
+    // Implementations for DataEntryFilter.
+
+    public boolean accepts(DataEntry dataEntry)
+    {
+        return dataEntry != null && stringMatcher.matches(dataEntry.getName());
+    }
+}
diff --git a/src/proguard/io/DataEntryParentFilter.java b/src/proguard/io/DataEntryParentFilter.java
new file mode 100644
index 0000000..8f2bff6
--- /dev/null
+++ b/src/proguard/io/DataEntryParentFilter.java
@@ -0,0 +1,51 @@
+/* $Id: DataEntryParentFilter.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+/**
+ * This DataEntryFilter delegates filtering to a DataEntryFilter for its parent.
+ *
+ * @author Eric Lafortune
+ */
+public class DataEntryParentFilter
+implements   DataEntryFilter
+{
+    private DataEntryFilter dataEntryFilter;
+
+
+    /**
+     * Creates a new ParentFilter.
+     * @param dataEntryFilter the filter that will be applied to the data
+     *                        entry's parent.
+     */
+    public DataEntryParentFilter(DataEntryFilter dataEntryFilter)
+    {
+        this.dataEntryFilter = dataEntryFilter;
+    }
+
+
+    // Implementations for DataEntryFilter.
+
+    public boolean accepts(DataEntry dataEntry)
+    {
+        return dataEntry != null && dataEntryFilter.accepts(dataEntry.getParent());
+    }
+}
diff --git a/src/proguard/io/DataEntryPump.java b/src/proguard/io/DataEntryPump.java
new file mode 100644
index 0000000..2b68072
--- /dev/null
+++ b/src/proguard/io/DataEntryPump.java
@@ -0,0 +1,43 @@
+/* $Id: DataEntryPump.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import java.io.*;
+
+
+/**
+ * This interface provides a method to pump data entries. The implementation
+ * determines the source and the type of the data entries. Typical examples
+ * are zip entries coming from a zip file of file entries coming from a
+ * directory structure. The reader can for instance collect the class files,
+ * or copy the resource files that are presented.
+ *
+ * @author Eric Lafortune
+ */
+public interface DataEntryPump
+{
+    /**
+     * Applies the given DataEntryReader to all data entries that the
+     * implementation can provide.
+     */
+    public void pumpDataEntries(DataEntryReader dataEntryReader)
+    throws IOException;
+}
diff --git a/src/proguard/io/DataEntryReader.java b/src/proguard/io/DataEntryReader.java
new file mode 100644
index 0000000..7a1a7d3
--- /dev/null
+++ b/src/proguard/io/DataEntryReader.java
@@ -0,0 +1,39 @@
+/* $Id: DataEntryReader.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import java.io.*;
+import java.util.zip.*;
+
+
+/**
+ * This interface provides methods for reading data entries. The implementation
+ * determines what to do with the read data, if anything.
+ *
+ * @author Eric Lafortune
+ */
+public interface DataEntryReader
+{
+    /**
+     * Reads the given data entry.
+     */
+    public void read(DataEntry dataEntry) throws IOException;
+}
diff --git a/src/proguard/io/DataEntryWriter.java b/src/proguard/io/DataEntryWriter.java
new file mode 100644
index 0000000..1677cc3
--- /dev/null
+++ b/src/proguard/io/DataEntryWriter.java
@@ -0,0 +1,65 @@
+/* $Id: DataEntryWriter.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import java.io.*;
+
+
+/**
+ * This interface provides methods for writing data entries, such as ZIP entries
+ * or files. The implementation determines to which type of data entry the
+ * data will be written.
+ *
+ * @author Eric Lafortune
+ */
+public interface DataEntryWriter
+{
+    /**
+     * Returns an output stream for writing data. The caller must not close
+     * the output stream; closing the output stream is the responsibility of
+     * the implementation of this interface.
+     * @param dataEntry the data entry for which the output stream is to be created.
+     * @return the output stream. The stream may be <code>null</code> to indicate
+     *         that the data entry should not be written.
+     */
+    public OutputStream getOutputStream(DataEntry dataEntry) throws IOException;
+
+
+    /**
+     * Returns an output stream for writing data. The caller must not close
+     * the output stream; closing the output stream is the responsibility of
+     * the implementation of this interface.
+     * @param dataEntry the data entry for which the output stream is to be created.
+     * @param finisher  the optional finisher that will be called before this
+     *                  class closes the output stream (at some later point in
+     *                  time) that will be returned (now).
+     * @return the output stream. The stream may be <code>null</code> to indicate
+     *         that the data entry should not be written.
+     */
+    public OutputStream getOutputStream(DataEntry dataEntry,
+                                        Finisher  finisher) throws IOException;
+
+
+    /**
+     * Finishes writing all data entries.
+     */
+    public void close() throws IOException;
+}
diff --git a/src/proguard/io/DirectoryPump.java b/src/proguard/io/DirectoryPump.java
new file mode 100644
index 0000000..1598338
--- /dev/null
+++ b/src/proguard/io/DirectoryPump.java
@@ -0,0 +1,74 @@
+/* $Id: DirectoryPump.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import java.io.*;
+
+
+/**
+ * This class can read a given file or directory, recursively, applying a given
+ * DataEntryReader to all files it comes across.
+ *
+ * @author Eric Lafortune
+ */
+public class DirectoryPump implements DataEntryPump
+{
+    private File directory;
+
+
+    public DirectoryPump(File directory)
+    {
+        this.directory = directory;
+    }
+
+
+    // Implementations for DataEntryPump.
+
+    public void pumpDataEntries(DataEntryReader dataEntryReader)
+    throws IOException
+    {
+        readFiles(directory, dataEntryReader);
+    }
+
+
+    /**
+     * Reads the given subdirectory recursively, applying the given DataEntryReader
+     * to all files that are encountered.
+     */
+    private void readFiles(File file, DataEntryReader dataEntryReader)
+    throws IOException
+    {
+        if (file.isDirectory())
+        {
+            // Recurse into the subdirectory.
+            File[] files = file.listFiles();
+
+            for (int index = 0; index < files.length; index++)
+            {
+                readFiles(files[index], dataEntryReader);
+            }
+        }
+        else
+        {
+            dataEntryReader.read(new FileDataEntry(directory, file));
+        }
+    }
+}
diff --git a/src/proguard/io/DirectoryWriter.java b/src/proguard/io/DirectoryWriter.java
new file mode 100644
index 0000000..536e4ed
--- /dev/null
+++ b/src/proguard/io/DirectoryWriter.java
@@ -0,0 +1,145 @@
+/* $Id: DirectoryWriter.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import proguard.classfile.*;
+
+import java.io.*;
+
+
+/**
+ * This DataEntryWriter writes sends data entries to individual files in a
+ * given directory.
+ *
+ * @author Eric Lafortune
+ */
+public class DirectoryWriter implements DataEntryWriter
+{
+    private File    baseFile;
+    private boolean isFile;
+
+    private File         currentFile;
+    private OutputStream currentOutputStream;
+    private Finisher     currentFinisher;
+
+
+    /**
+     * Creates a new DirectoryWriter.
+     * @param baseFile the base directory to which all files will be written.
+     */
+    public DirectoryWriter(File    baseFile,
+                           boolean isFile)
+    {
+        this.baseFile = baseFile;
+        this.isFile   = isFile;
+    }
+
+
+    // Implementations for DataEntryWriter.
+
+    public OutputStream getOutputStream(DataEntry dataEntry) throws IOException
+    {
+        return getOutputStream(dataEntry,  null);
+    }
+
+
+    public OutputStream getOutputStream(DataEntry dataEntry,
+                                        Finisher  finisher) throws IOException
+    {
+        // Should we close the current file?
+        if (!isFile             &&
+            currentFile != null &&
+            !currentFile.equals(getFile(dataEntry)))
+        {
+            closeEntry();
+        }
+
+        // Do we need a new stream?
+        if (currentOutputStream == null)
+        {
+            File file = getFile(dataEntry);
+
+            // Make sure the parent directories exist.
+            File parentDirectory = file.getParentFile();
+            if (parentDirectory != null   &&
+                !parentDirectory.exists() &&
+                !parentDirectory.mkdirs())
+            {
+                throw new IOException("Can't create directory [" + parentDirectory.getPath() + "]");
+            }
+
+            // Open a new output stream for writing to the file.
+            currentOutputStream =
+                new BufferedOutputStream(
+                new FileOutputStream(file));
+
+            currentFinisher = finisher;
+            currentFile     = file;
+        }
+
+        return currentOutputStream;
+    }
+
+
+    public void close() throws IOException
+    {
+        // Close the file stream, if any.
+        closeEntry();
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns the file for the given data entry.
+     */
+    private File getFile(DataEntry dataEntry)
+    {
+        // Use the specified file, or construct a new file.
+        return isFile ?
+            baseFile :
+            new File(baseFile,
+                     dataEntry.getName().replace(ClassConstants.INTERNAL_PACKAGE_SEPARATOR,
+                                                 File.separatorChar));
+    }
+
+
+    /**
+     * Closes the previous file, if any.
+     */
+    private void closeEntry() throws IOException
+    {
+        // Close the file stream, if any.
+        if (currentOutputStream != null)
+        {
+            // Let any finisher finish up first.
+            if (currentFinisher != null)
+            {
+                currentFinisher.finish();
+                currentFinisher = null;
+            }
+
+            currentOutputStream.close();
+            currentOutputStream = null;
+            currentFile         = null;
+        }
+    }
+}
diff --git a/src/proguard/io/FileDataEntry.java b/src/proguard/io/FileDataEntry.java
new file mode 100644
index 0000000..d5b794e
--- /dev/null
+++ b/src/proguard/io/FileDataEntry.java
@@ -0,0 +1,92 @@
+/* $Id: FileDataEntry.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import proguard.classfile.ClassConstants;
+
+import java.io.*;
+
+/**
+ * This <code>DataEntry</code> represents a file.
+ *
+ * @author Eric Lafortune
+ */
+public class FileDataEntry implements DataEntry
+{
+    private File        directory;
+    private File        file;
+    private InputStream inputStream;
+
+
+    public FileDataEntry(File directory,
+                         File file)
+    {
+        this.directory = directory;
+        this.file      = file;
+    }
+
+
+    // Implementations for DataEntry.
+
+    public String getName()
+    {
+        // Chop the directory name from the file name and get the right separators.
+        return file.equals(directory) ?
+            file.getName() :
+            file.getPath()
+            .substring(directory.getPath().length() + File.separator.length())
+            .replace(File.separatorChar, ClassConstants.INTERNAL_PACKAGE_SEPARATOR);
+    }
+
+
+    // Implementations for InputEntry.
+
+    public InputStream getInputStream() throws IOException
+    {
+        if (inputStream == null)
+        {
+            inputStream = new BufferedInputStream(new FileInputStream(file));
+        }
+
+        return inputStream;
+    }
+
+
+    public void closeInputStream() throws IOException
+    {
+        inputStream.close();
+        inputStream = null;
+    }
+
+
+    public DataEntry getParent()
+    {
+        return null;
+    }
+
+
+    // Implementations for Object.
+
+    public String toString()
+    {
+        return getName();
+    }
+}
diff --git a/src/proguard/io/FilteredDataEntryReader.java b/src/proguard/io/FilteredDataEntryReader.java
new file mode 100644
index 0000000..81cf923
--- /dev/null
+++ b/src/proguard/io/FilteredDataEntryReader.java
@@ -0,0 +1,96 @@
+/* $Id: FilteredDataEntryReader.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import java.io.*;
+
+
+/**
+ * This DataEntryReader delegates to one of two other DataEntryReader instances,
+ * depending on whether the data entry passes through a given data entry filter
+ * or not.
+ *
+ * @author Eric Lafortune
+ */
+public class FilteredDataEntryReader implements DataEntryReader
+{
+    private DataEntryFilter dataEntryFilter;
+    private DataEntryReader acceptedDataEntryReader;
+    private DataEntryReader rejectedDataEntryReader;
+
+
+    /**
+     * Creates a new FilteredDataEntryReader with only a reader for accepted
+     * data entries.
+     * @param dataEntryFilter         the data entry filter.
+     * @param acceptedDataEntryReader the DataEntryReader to which the reading
+     *                                will be delegated if the filter accepts
+     *                                the data entry. May be <code>null</code>.
+     */
+    public FilteredDataEntryReader(DataEntryFilter dataEntryFilter,
+                                   DataEntryReader acceptedDataEntryReader)
+    {
+        this(dataEntryFilter, acceptedDataEntryReader, null);
+    }
+
+
+    /**
+     * Creates a new FilteredDataEntryReader.
+     * @param dataEntryFilter         the data entry filter.
+     * @param acceptedDataEntryReader the DataEntryReader to which the reading
+     *                                will be delegated if the filter accepts
+     *                                the data entry. May be <code>null</code>.
+     * @param rejectedDataEntryReader the DataEntryReader to which the reading
+     *                                will be delegated if the filter does not
+     *                                accept the data entry. May be
+     *                                <code>null</code>.
+     */
+    public FilteredDataEntryReader(DataEntryFilter dataEntryFilter,
+                                   DataEntryReader acceptedDataEntryReader,
+                                   DataEntryReader rejectedDataEntryReader)
+    {
+        this.dataEntryFilter         = dataEntryFilter;
+        this.acceptedDataEntryReader = acceptedDataEntryReader;
+        this.rejectedDataEntryReader = rejectedDataEntryReader;
+    }
+
+
+    // Implementations for DataEntryReader.
+
+    public void read(DataEntry dataEntry)
+    throws IOException
+    {
+        if (dataEntryFilter.accepts(dataEntry))
+        {
+            if (acceptedDataEntryReader != null)
+            {
+                acceptedDataEntryReader.read(dataEntry);
+            }
+        }
+        else
+        {
+            if (rejectedDataEntryReader != null)
+            {
+                rejectedDataEntryReader.read(dataEntry);
+            }
+        }
+    }
+}
diff --git a/src/proguard/io/FilteredDataEntryWriter.java b/src/proguard/io/FilteredDataEntryWriter.java
new file mode 100644
index 0000000..ffd0dc3
--- /dev/null
+++ b/src/proguard/io/FilteredDataEntryWriter.java
@@ -0,0 +1,112 @@
+/* $Id: FilteredDataEntryWriter.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import java.io.*;
+
+/**
+ * This DataEntryWriter delegates to one of two other DataEntryWriter instances,
+ * depending on whether the data entry passes through a given data entry filter
+ * or not.
+ *
+ * @author Eric Lafortune
+ */
+public class FilteredDataEntryWriter implements DataEntryWriter
+{
+    private DataEntryFilter dataEntryFilter;
+    private DataEntryWriter acceptedDataEntryWriter;
+    private DataEntryWriter rejectedDataEntryWriter;
+
+
+    /**
+     * Creates a new FilteredDataEntryWriter with only a writer for accepted
+     * data entries.
+     * @param dataEntryFilter         the data entry filter.
+     * @param acceptedDataEntryWriter the DataEntryWriter to which the writing
+     *                                will be delegated if the filter accepts
+     *                                the data entry. May be <code>null</code>.
+     */
+    public FilteredDataEntryWriter(DataEntryFilter dataEntryFilter,
+                                   DataEntryWriter acceptedDataEntryWriter)
+    {
+        this(dataEntryFilter, acceptedDataEntryWriter, null);
+    }
+
+
+    /**
+     * Creates a new FilteredDataEntryWriter.
+     * @param dataEntryFilter         the data entry filter.
+     * @param acceptedDataEntryWriter the DataEntryWriter to which the writing
+     *                                will be delegated if the filter accepts
+     *                                the data entry. May be <code>null</code>.
+     * @param rejectedDataEntryWriter the DataEntryWriter to which the writing
+     *                                will be delegated if the filter does not
+     *                                accept the data entry. May be
+     *                                <code>null</code>.
+     */
+    public FilteredDataEntryWriter(DataEntryFilter dataEntryFilter,
+                                   DataEntryWriter acceptedDataEntryWriter,
+                                   DataEntryWriter rejectedDataEntryWriter)
+    {
+        this.dataEntryFilter         = dataEntryFilter;
+        this.acceptedDataEntryWriter = acceptedDataEntryWriter;
+        this.rejectedDataEntryWriter = rejectedDataEntryWriter;
+    }
+
+
+    // Implementations for DataEntryWriter.
+
+    public OutputStream getOutputStream(DataEntry dataEntry) throws IOException
+    {
+        return getOutputStream(dataEntry,  null);
+    }
+
+
+    public OutputStream getOutputStream(DataEntry dataEntry,
+                                        Finisher  finisher) throws IOException
+    {
+        // Get the right data entry writer.
+        DataEntryWriter dataEntryWriter = dataEntryFilter.accepts(dataEntry) ?
+            acceptedDataEntryWriter :
+            rejectedDataEntryWriter;
+
+        // Delegate to it, if it's not null.
+        return dataEntryWriter != null ?
+            dataEntryWriter.getOutputStream(dataEntry, finisher) :
+            null;
+    }
+
+
+    public void close() throws IOException
+    {
+        if (acceptedDataEntryWriter != null)
+        {
+            acceptedDataEntryWriter.close();
+            acceptedDataEntryWriter = null;
+        }
+
+        if (rejectedDataEntryWriter != null)
+        {
+            rejectedDataEntryWriter.close();
+            rejectedDataEntryWriter = null;
+        }
+    }
+}
diff --git a/src/proguard/io/Finisher.java b/src/proguard/io/Finisher.java
new file mode 100644
index 0000000..18b2413
--- /dev/null
+++ b/src/proguard/io/Finisher.java
@@ -0,0 +1,37 @@
+/* $Id: Finisher.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import java.io.*;
+
+/**
+ * This interface specifies a listener that is called to finish an output stream
+ * before it is closed.
+ *
+ * @author Eric Lafortune
+ */
+public interface Finisher
+{
+    /**
+     * Finishes an output stream right before it is closed.
+     */
+    public void finish() throws IOException;
+}
diff --git a/src/proguard/io/JarReader.java b/src/proguard/io/JarReader.java
new file mode 100644
index 0000000..3210122
--- /dev/null
+++ b/src/proguard/io/JarReader.java
@@ -0,0 +1,78 @@
+/* $Id: JarReader.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import java.io.*;
+import java.util.zip.*;
+
+/**
+ * This DataEntryReader lets a given DataEntryReader read all data entries of
+ * the read jar/war/zip data entries.
+ *
+ * @author Eric Lafortune
+ */
+public class JarReader implements DataEntryReader
+{
+    DataEntryReader dataEntryReader;
+
+
+    /**
+     * Creates a new JarReader.
+     */
+    public JarReader(DataEntryReader dataEntryReader)
+    {
+        this.dataEntryReader = dataEntryReader;
+    }
+
+
+    // Implementation for DataEntryReader.
+
+    public void read(DataEntry dataEntry) throws IOException
+    {
+        ZipInputStream zipInputStream = new ZipInputStream(dataEntry.getInputStream());
+
+        try
+        {
+            // Get all entries from the input jar.
+            while (true)
+            {
+                // Can we get another entry?
+                ZipEntry zipEntry = zipInputStream.getNextEntry();
+                if (zipEntry == null)
+                {
+                    break;
+                }
+
+                if (!zipEntry.isDirectory())
+                {
+                    // Delegate the actual reading to the data entry reader.
+                    dataEntryReader.read(new ZipDataEntry(dataEntry,
+                                                          zipEntry,
+                                                          zipInputStream));
+                }
+            }
+        }
+        finally
+        {
+            dataEntry.closeInputStream();
+        }
+    }
+}
diff --git a/src/proguard/io/JarWriter.java b/src/proguard/io/JarWriter.java
new file mode 100644
index 0000000..9e62949
--- /dev/null
+++ b/src/proguard/io/JarWriter.java
@@ -0,0 +1,183 @@
+/* $Id: JarWriter.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import java.io.*;
+import java.util.jar.*;
+import java.util.*;
+import java.util.zip.*;
+
+
+/**
+ * This DataEntryWriter sends data entries to a given jar/zip file.
+ * The manifest and comment properties can optionally be set.
+ *
+ * @author Eric Lafortune
+ */
+public class JarWriter implements DataEntryWriter, Finisher
+{
+    private DataEntryWriter dataEntryWriter;
+    private Manifest        manifest;
+    private String          comment;
+
+    private OutputStream    currentParentOutputStream;
+    private ZipOutputStream currentJarOutputStream;
+    private Finisher        currentFinisher;
+    private String          currentEntryName;
+
+    // The names of the jar entries that are already in the jar.
+    private Set jarEntryNames = new HashSet();
+
+
+    /**
+     * Creates a new JarWriter without manifest or comment.
+     */
+    public JarWriter(DataEntryWriter dataEntryWriter)
+    {
+        this(dataEntryWriter, null, null);
+    }
+
+
+    /**
+     * Creates a new JarWriter.
+     */
+    public JarWriter(DataEntryWriter dataEntryWriter,
+                     Manifest        manifest,
+                     String          comment)
+    {
+        this.dataEntryWriter = dataEntryWriter;
+        this.manifest        = manifest;
+        this.comment         = comment;
+    }
+
+
+    // Implementations for DataEntryWriter.
+
+    public OutputStream getOutputStream(DataEntry dataEntry) throws IOException
+    {
+        return getOutputStream(dataEntry,  null);
+    }
+
+
+    public OutputStream getOutputStream(DataEntry dataEntry,
+                                        Finisher  finisher) throws IOException
+    {
+        // Get the parent stream, new or exisiting.
+        // This may finish our own jar output stream.
+        OutputStream parentOutputStream =
+            dataEntryWriter.getOutputStream(dataEntry.getParent(), this);
+
+        // Did we get a stream?
+        if (parentOutputStream == null)
+        {
+            return null;
+        }
+
+        // Do we need a new stream?
+        if (currentParentOutputStream == null)
+        {
+            currentParentOutputStream = parentOutputStream;
+
+            // Create a new jar stream, with a manifest, if set.
+            currentJarOutputStream = manifest != null ?
+                new JarOutputStream(parentOutputStream, manifest) :
+                new ZipOutputStream(parentOutputStream);
+
+            // Add a comment, if set.
+            if (comment != null)
+            {
+                currentJarOutputStream.setComment(comment);
+            }
+        }
+
+        // Get the entry name.
+        String name = dataEntry.getName();
+
+        // Do we need a new entry?
+        if (!name.equals(currentEntryName))
+        {
+            // Close the previous ZIP entry, if any.
+            closeEntry();
+
+            // We have to check if the name is already used, because ZipOutputStream
+            // doesn't handle this case properly (it throws an exception which can
+            // be caught, but the ZipDataEntry is remembered anyway).
+            if (!jarEntryNames.add(name))
+            {
+                throw new IOException("Duplicate zip entry ["+dataEntry+"]");
+            }
+
+            // Create a new entry.
+            currentJarOutputStream.putNextEntry(new ZipEntry(name));
+
+            currentFinisher  = finisher;
+            currentEntryName = name;
+        }
+
+        return currentJarOutputStream;
+    }
+
+
+    public void finish() throws IOException
+    {
+        // Finish the entire ZIP stream, if any.
+        if (currentJarOutputStream != null)
+        {
+            // Close the previous ZIP entry, if any.
+            closeEntry();
+
+            // Finish the entire ZIP stream.
+            currentJarOutputStream.finish();
+            currentJarOutputStream    = null;
+            currentParentOutputStream = null;
+            jarEntryNames.clear();
+        }
+    }
+
+
+    public void close() throws IOException
+    {
+        // Close the parent stream.
+        dataEntryWriter.close();
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Closes the previous ZIP entry, if any.
+     */
+    private void closeEntry() throws IOException
+    {
+        if (currentEntryName != null)
+        {
+            // Let any finisher finish up first.
+            if (currentFinisher != null)
+            {
+                currentFinisher.finish();
+                currentFinisher = null;
+            }
+
+            currentJarOutputStream.closeEntry();
+            currentEntryName = null;
+        }
+    }
+}
diff --git a/src/proguard/io/ParentDataEntryWriter.java b/src/proguard/io/ParentDataEntryWriter.java
new file mode 100644
index 0000000..097965b
--- /dev/null
+++ b/src/proguard/io/ParentDataEntryWriter.java
@@ -0,0 +1,67 @@
+/* $Id: ParentDataEntryWriter.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import java.io.*;
+
+/**
+ * This DataEntryWriter lets another DataEntryWriter write the parent data
+ * entries.
+ *
+ * @author Eric Lafortune
+ */
+public class ParentDataEntryWriter implements DataEntryWriter
+{
+    private DataEntryWriter dataEntryWriter;
+
+
+    /**
+     * Creates a new ParentDataEntryWriter.
+     * @param dataEntryWriter the DataEntryWriter to which the writing will be
+     *                        delegated, passing the data entries' parents.
+     */
+    public ParentDataEntryWriter(DataEntryWriter dataEntryWriter)
+    {
+        this.dataEntryWriter = dataEntryWriter;
+    }
+
+
+    // Implementations for DataEntryWriter.
+
+    public OutputStream getOutputStream(DataEntry dataEntry) throws IOException
+    {
+        return getOutputStream(dataEntry, null);
+    }
+
+
+    public OutputStream getOutputStream(DataEntry dataEntry,
+                                        Finisher  finisher) throws IOException
+    {
+        return dataEntryWriter.getOutputStream(dataEntry.getParent(),
+                                               finisher);
+    }
+
+
+    public void close() throws IOException
+    {
+        dataEntryWriter.close();
+    }
+}
diff --git a/src/proguard/io/RenamedDataEntry.java b/src/proguard/io/RenamedDataEntry.java
new file mode 100644
index 0000000..79be047
--- /dev/null
+++ b/src/proguard/io/RenamedDataEntry.java
@@ -0,0 +1,77 @@
+/* $Id: RenamedDataEntry.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import java.io.*;
+
+/**
+ * This DataEntry wraps another data entry, returning a different name instead
+ * of the wrapped data entry's name.
+ *
+ * @author Eric Lafortune
+ */
+public class RenamedDataEntry implements DataEntry
+{
+    private DataEntry dataEntry;
+    private String    name;
+
+
+    public RenamedDataEntry(DataEntry dataEntry,
+                            String    name)
+    {
+        this.dataEntry = dataEntry;
+        this.name      = name;
+    }
+
+
+    // Implementations for DataEntry.
+
+    public String getName()
+    {
+        return name;
+    }
+
+
+    public InputStream getInputStream() throws IOException
+    {
+        return dataEntry.getInputStream();
+    }
+
+
+    public void closeInputStream() throws IOException
+    {
+        dataEntry.closeInputStream();
+    }
+
+
+    public DataEntry getParent()
+    {
+        return dataEntry.getParent();
+    }
+
+
+    // Implementations for Object.
+
+    public String toString()
+    {
+        return name + " == " + dataEntry;
+    }
+}
diff --git a/src/proguard/io/ZipDataEntry.java b/src/proguard/io/ZipDataEntry.java
new file mode 100644
index 0000000..ff7a3bc
--- /dev/null
+++ b/src/proguard/io/ZipDataEntry.java
@@ -0,0 +1,85 @@
+/* $Id: ZipDataEntry.java,v 1.3 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import java.io.*;
+import java.util.zip.*;
+
+import proguard.classfile.ClassConstants;
+
+/**
+ * This <code>DataEntry</code> represents a ZIP entry.
+ *
+ * @author Eric Lafortune
+ */
+public class ZipDataEntry implements DataEntry
+{
+    private DataEntry      parent;
+    private ZipEntry       zipEntry;
+    private ZipInputStream zipInputStream;
+
+
+    public ZipDataEntry(DataEntry      parent,
+                        ZipEntry       zipEntry,
+                        ZipInputStream zipInputStream)
+    {
+        this.parent         = parent;
+        this.zipEntry       = zipEntry;
+        this.zipInputStream = zipInputStream;
+    }
+
+
+    // Implementations for DataEntry.
+
+    public String getName()
+    {
+        // Chop the directory name from the file name and get the right separators.
+        return zipEntry.getName()
+            .replace(File.separatorChar, ClassConstants.INTERNAL_PACKAGE_SEPARATOR);
+    }
+
+
+    public InputStream getInputStream() throws IOException
+    {
+        return zipInputStream;
+    }
+
+
+    public void closeInputStream() throws IOException
+    {
+        zipInputStream.closeEntry();
+        zipInputStream = null;
+    }
+
+
+    public DataEntry getParent()
+    {
+        return parent;
+    }
+
+
+    // Implementations for Object.
+
+    public String toString()
+    {
+        return parent.toString() + ':' + getName();
+    }
+}
diff --git a/src/proguard/io/package.html b/src/proguard/io/package.html
new file mode 100644
index 0000000..4ad9f41
--- /dev/null
+++ b/src/proguard/io/package.html
@@ -0,0 +1,4 @@
+<body>
+This package contains classes to read and write files, optionally wrapped in
+jars, wars, ears, zips, directories,...
+</body>
diff --git a/src/proguard/obfuscate/AttributeShrinker.java b/src/proguard/obfuscate/AttributeShrinker.java
new file mode 100644
index 0000000..1fb95ef
--- /dev/null
+++ b/src/proguard/obfuscate/AttributeShrinker.java
@@ -0,0 +1,160 @@
+/* $Id: AttributeShrinker.java,v 1.16 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This ClassFileVisitor removes attributes that are not marked
+ * as being used or required.
+ *
+ * @see AttributeUsageMarker
+ *
+ * @author Eric Lafortune
+ */
+public class AttributeShrinker
+  implements ClassFileVisitor,
+             MemberInfoVisitor,
+             AttrInfoVisitor
+{
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Compact the array for class attributes.
+        programClassFile.u2attributesCount =
+            shrinkArray(programClassFile.attributes,
+                        programClassFile.u2attributesCount);
+
+        // Compact the attributes in fields, methods, and class attributes,
+        programClassFile.fieldsAccept(this);
+        programClassFile.methodsAccept(this);
+        programClassFile.attributesAccept(this);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        // Library class files are left unchanged.
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        visitMemberInfo(programClassFile, programFieldInfo);
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        visitMemberInfo(programClassFile, programMethodInfo);
+    }
+
+
+    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    {
+        // Compact the attributes array.
+        programMemberInfo.u2attributesCount =
+            shrinkArray(programMemberInfo.attributes,
+                        programMemberInfo.u2attributesCount);
+
+        // Compact any attributes of the remaining attributes.
+        programMemberInfo.attributesAccept(programClassFile, this);
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    {
+        // Library class files are left unchanged.
+    }
+
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        // Library class files are left unchanged.
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
+
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        // Compact the attributes array.
+        codeAttrInfo.u2attributesCount =
+            shrinkArray(codeAttrInfo.attributes,
+                        codeAttrInfo.u2attributesCount);
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Removes all VisitorAccepter objects that are not marked as being used
+     * from the given array.
+     * @return the new number of VisitorAccepter objects.
+     */
+    private static int shrinkArray(VisitorAccepter[] array, int length)
+    {
+        int counter = 0;
+
+        // Shift the used objects together.
+        for (int index = 0; index < length; index++)
+        {
+            if (AttributeUsageMarker.isUsed(array[index]))
+            {
+                array[counter++] = array[index];
+            }
+        }
+
+        // Clear the remaining array elements.
+        for (int index = counter; index < length; index++)
+        {
+            array[index] = null;
+        }
+
+        return counter;
+    }
+}
diff --git a/src/proguard/obfuscate/AttributeUsageMarker.java b/src/proguard/obfuscate/AttributeUsageMarker.java
new file mode 100644
index 0000000..4f8ecb0
--- /dev/null
+++ b/src/proguard/obfuscate/AttributeUsageMarker.java
@@ -0,0 +1,401 @@
+/* $Id: AttributeUsageMarker.java,v 1.23 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.visitor.*;
+import proguard.util.*;
+
+import java.util.*;
+
+
+/**
+ * This ClassFileVisitor marks all attributes that should be kept in the classes
+ * it visits.
+ *
+ * @see AttributeShrinker
+ *
+ * @author Eric Lafortune
+ */
+public class AttributeUsageMarker
+  implements ClassFileVisitor,
+             MemberInfoVisitor,
+             AttrInfoVisitor,
+             InnerClassesInfoVisitor
+{
+    // A visitor info flag to indicate the attribute is being used.
+    private static final Object USED = new Object();
+
+
+    // Flags to specify whether optional attributes should be kept anyway.
+    private boolean       keepAllAttributes;
+    private boolean       keepAllUnknownAttributes;
+    private boolean       keepAllKnownAttributes;
+    private StringMatcher keepAttributes;
+
+    private boolean       keepInnerClassNameAttribute;
+    private boolean       keepEnclosingMethodAttribute;
+    private boolean       keepLineNumberTableAttribute;
+    private boolean       keepLocalVariableTableAttribute;
+    private boolean       keepLocalVariableTypeTableAttribute;
+    private boolean       keepSourceFileAttribute;
+    private boolean       keepSourceDirAttribute;
+    private boolean       keepDeprecatedAttribute;
+    private boolean       keepSyntheticAttribute;
+    private boolean       keepSignatureAttribute;
+    private boolean       keepRuntimeVisibleAnnotationsAttribute;
+    private boolean       keepRuntimeInvisibleAnnotationsAttribute;
+    private boolean       keepRuntimeVisibleParameterAnnotationsAttribute;
+    private boolean       keepRuntimeInvisibleParameterAnnotationsAttribute;
+    private boolean       keepAnnotationDefaultAttribute;
+
+
+    /**
+     * Specifies to keep all optional attributes.
+     */
+    public void setKeepAllAttributes()
+    {
+        keepAllAttributes = true;
+    }
+
+    /**
+     * Specifies to keep all unknown attributes.
+     */
+    public void setKeepAllUnknownAttributes()
+    {
+        keepAllUnknownAttributes = true;
+    }
+
+    /**
+     * Specifies to keep all known attributes.
+     */
+    public void setKeepAllKnownAttributes()
+    {
+        keepAllKnownAttributes = true;
+    }
+
+
+    /**
+     * Specifies to keep optional attributes with the given names. The attribute
+     * names may contain "*" or "?" wildcards, and they may be preceded by the
+     * "!" negator.
+     */
+    public void setKeepAttributes(List attributeNames)
+    {
+        keepAttributes = new BasicListMatcher(attributeNames);
+
+        // Precompute whether the list of attribute names matches the supported
+        // attributes.
+        keepInnerClassNameAttribute                       = keepAttributes.matches(ClassConstants.ATTR_InnerClasses);
+        keepEnclosingMethodAttribute                      = keepAttributes.matches(ClassConstants.ATTR_EnclosingMethod);
+        keepLineNumberTableAttribute                      = keepAttributes.matches(ClassConstants.ATTR_LineNumberTable);
+        keepLocalVariableTableAttribute                   = keepAttributes.matches(ClassConstants.ATTR_LocalVariableTable);
+        keepLocalVariableTypeTableAttribute               = keepAttributes.matches(ClassConstants.ATTR_LocalVariableTypeTable);
+        keepSourceFileAttribute                           = keepAttributes.matches(ClassConstants.ATTR_SourceFile);
+        keepSourceDirAttribute                            = keepAttributes.matches(ClassConstants.ATTR_SourceDir);
+        keepDeprecatedAttribute                           = keepAttributes.matches(ClassConstants.ATTR_Deprecated);
+        keepSyntheticAttribute                            = keepAttributes.matches(ClassConstants.ATTR_Synthetic);
+        keepSignatureAttribute                            = keepAttributes.matches(ClassConstants.ATTR_Signature);
+        keepRuntimeVisibleAnnotationsAttribute            = keepAttributes.matches(ClassConstants.ATTR_RuntimeVisibleAnnotations);
+        keepRuntimeInvisibleAnnotationsAttribute          = keepAttributes.matches(ClassConstants.ATTR_RuntimeInvisibleAnnotations);
+        keepRuntimeVisibleParameterAnnotationsAttribute   = keepAttributes.matches(ClassConstants.ATTR_RuntimeVisibleParameterAnnotations);
+        keepRuntimeInvisibleParameterAnnotationsAttribute = keepAttributes.matches(ClassConstants.ATTR_RuntimeInvisibleParameterAnnotations);
+        keepAnnotationDefaultAttribute                    = keepAttributes.matches(ClassConstants.ATTR_AnnotationDefault);
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Mark the class member attributes that should be kept.
+        programClassFile.fieldsAccept(this);
+        programClassFile.methodsAccept(this);
+
+        // Mark the class attributes that should be kept.
+        programClassFile.attributesAccept(this);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile) {}
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        visitMemberInfo(programClassFile, programFieldInfo);
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        visitMemberInfo(programClassFile, programMethodInfo);
+    }
+
+
+    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    {
+        // Mark the class member attributes that should be kept.
+        programMemberInfo.attributesAccept(programClassFile, this);
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo)
+    {
+        if (keepAllAttributes ||
+            keepAllUnknownAttributes ||
+            (keepAttributes != null &&
+             keepAttributes.matches(unknownAttrInfo.getAttributeName(classFile))))
+        {
+            markAsUsed(unknownAttrInfo);
+        }
+    }
+
+
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
+    {
+        markAsUsed(innerClassesAttrInfo);
+
+        if (!keepAllAttributes &&
+            !keepAllKnownAttributes &&
+            !keepInnerClassNameAttribute)
+        {
+            // Clear references to the original inner class names.
+            innerClassesAttrInfo.innerClassEntriesAccept(classFile, this);
+        }
+    }
+
+
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
+    {
+        if (keepAllAttributes      ||
+            keepAllKnownAttributes ||
+            keepEnclosingMethodAttribute)
+        {
+            markAsUsed(enclosingMethodAttrInfo);
+        }
+    }
+
+
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo)
+    {
+        markAsUsed(constantValueAttrInfo);
+    }
+
+
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo)
+    {
+        markAsUsed(exceptionsAttrInfo);
+    }
+
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        markAsUsed(codeAttrInfo);
+
+        // Mark the code attributes that should be kept.
+        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
+    }
+
+
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo)
+    {
+        if (keepAllAttributes      ||
+            keepAllKnownAttributes ||
+            keepLineNumberTableAttribute)
+        {
+            markAsUsed(lineNumberTableAttrInfo);
+        }
+    }
+
+
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
+    {
+        if (keepAllAttributes      ||
+            keepAllKnownAttributes ||
+            keepLocalVariableTableAttribute)
+        {
+            markAsUsed(localVariableTableAttrInfo);
+        }
+    }
+
+
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
+    {
+        if (keepAllAttributes      ||
+            keepAllKnownAttributes ||
+            keepLocalVariableTypeTableAttribute)
+        {
+            markAsUsed(localVariableTypeTableAttrInfo);
+        }
+    }
+
+
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo)
+    {
+        if (keepAllAttributes      ||
+            keepAllKnownAttributes ||
+            keepSourceFileAttribute)
+        {
+            markAsUsed(sourceFileAttrInfo);
+        }
+    }
+
+
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo)
+    {
+        if (keepAllAttributes      ||
+            keepAllKnownAttributes ||
+            keepSourceDirAttribute)
+        {
+            markAsUsed(sourceDirAttrInfo);
+        }
+    }
+
+
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo)
+    {
+        if (keepAllAttributes      ||
+            keepAllKnownAttributes ||
+            keepDeprecatedAttribute)
+        {
+            markAsUsed(deprecatedAttrInfo);
+        }
+    }
+
+
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo)
+    {
+        if (keepAllAttributes      ||
+            keepAllKnownAttributes ||
+            keepSyntheticAttribute)
+        {
+            markAsUsed(syntheticAttrInfo);
+        }
+    }
+
+
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
+    {
+        if (keepAllAttributes      ||
+            keepAllKnownAttributes ||
+            keepSignatureAttribute)
+        {
+            markAsUsed(signatureAttrInfo);
+        }
+    }
+
+
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
+    {
+        if (keepAllAttributes      ||
+            keepAllKnownAttributes ||
+            keepRuntimeVisibleAnnotationsAttribute)
+        {
+            markAsUsed(runtimeVisibleAnnotationsAttrInfo);
+        }
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
+    {
+        if (keepAllAttributes      ||
+            keepAllKnownAttributes ||
+            keepRuntimeInvisibleAnnotationsAttribute)
+        {
+            markAsUsed(runtimeInvisibleAnnotationsAttrInfo);
+        }
+    }
+
+
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
+    {
+        if (keepAllAttributes      ||
+            keepAllKnownAttributes ||
+            keepRuntimeVisibleParameterAnnotationsAttribute)
+        {
+            markAsUsed(runtimeVisibleParameterAnnotationsAttrInfo);
+        }
+    }
+
+
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
+    {
+        if (keepAllAttributes      ||
+            keepAllKnownAttributes ||
+            keepRuntimeInvisibleParameterAnnotationsAttribute)
+        {
+            markAsUsed(runtimeInvisibleParameterAnnotationsAttrInfo);
+        }
+    }
+
+
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
+    {
+        if (keepAllAttributes      ||
+            keepAllKnownAttributes ||
+            keepAnnotationDefaultAttribute)
+        {
+            markAsUsed(annotationDefaultAttrInfo);
+        }
+    }
+
+
+    // Implementations for InnerClassesInfoVisitor.
+
+    public void visitInnerClassesInfo(ClassFile classFile, InnerClassesInfo innerClassesInfo)
+    {
+        // Clear the reference to the original inner class name, as used in
+        // the source code.
+        innerClassesInfo.u2innerNameIndex = 0;
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Marks the given VisitorAccepter as being used (or useful).
+     * In this context, the VisitorAccepter will be an AttrInfo object.
+     */
+    private static void markAsUsed(VisitorAccepter visitorAccepter)
+    {
+        visitorAccepter.setVisitorInfo(USED);
+    }
+
+
+    /**
+     * Returns whether the given VisitorAccepter has been marked as being used.
+     * In this context, the VisitorAccepter will be an AttrInfo object.
+     */
+    static boolean isUsed(VisitorAccepter visitorAccepter)
+    {
+        return visitorAccepter.getVisitorInfo() == USED;
+    }
+}
diff --git a/src/proguard/obfuscate/ClassFileObfuscator.java b/src/proguard/obfuscate/ClassFileObfuscator.java
new file mode 100644
index 0000000..98f0275
--- /dev/null
+++ b/src/proguard/obfuscate/ClassFileObfuscator.java
@@ -0,0 +1,179 @@
+/* $Id: ClassFileObfuscator.java,v 1.23 2004/11/01 21:17:51 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+
+import java.util.*;
+
+
+/**
+ * This <code>ClassFileVisitor</code> comes up with obfuscated names for the
+ * class files it visits, and for their class members. The actual renaming is
+ * done afterward.
+ *
+ * @see ClassFileRenamer
+ *
+ * @author Eric Lafortune
+ */
+public class ClassFileObfuscator
+  implements ClassFileVisitor
+{
+    private boolean useMixedCaseClassNames;
+    private String  defaultPackageName;
+
+    // Map: [package name - class name factory]
+    private final Map         packageMap = new HashMap();
+    private final NameFactory defaultPackageClassNameFactory;
+    private final Set         namesToAvoid = new HashSet();
+
+
+    /**
+     * Creates a new ClassFileObfuscator.
+     * @param programClassPool   the class pool in which class names have to be
+     *                           unique.
+     * @param defaultPackageName the package in which all classes that don't
+     *                           have fixed names will be put, or <code>null</code>,
+     *                           if all classes can remain in their original
+     *                           packages.
+     * @param allowAggressiveOverloading a flag that specifies whether class
+     *                           members can be overloaded aggressively.
+     */
+    public ClassFileObfuscator(ClassPool programClassPool,
+                               String    defaultPackageName,
+                               boolean   useMixedCaseClassNames)
+    {
+        this.defaultPackageName             = defaultPackageName;
+        this.useMixedCaseClassNames         = useMixedCaseClassNames;
+        this.defaultPackageClassNameFactory = new SimpleNameFactory(useMixedCaseClassNames);
+
+        // Collect all names that have been taken already.
+        programClassPool.classFilesAccept(new ClassFileVisitor()
+        {
+            public void visitProgramClassFile(ProgramClassFile programClassFile)
+            {
+                String newClassName = newClassName(programClassFile);
+                if (newClassName != null)
+                {
+                    namesToAvoid.add(newClassName);
+                }
+            }
+
+            public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+            {
+            }
+        });
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Does this class file still need a new name?
+        if (newClassName(programClassFile) == null)
+        {
+            // Figure out a new name.
+            String className   = programClassFile.getName();
+            String packageName = ClassUtil.internalPackageName(className);
+
+            String newPackageName = packageName;
+
+            // Find the right name factory for this package, or use the default.
+            NameFactory packageClassNameFactory = (NameFactory)packageMap.get(packageName);
+            if (packageClassNameFactory == null)
+            {
+                // Do we have a default package name?
+                if (defaultPackageName == null)
+                {
+                    // We haven't seen this package before. Create a new name factory
+                    // for it.
+                    packageClassNameFactory = new SimpleNameFactory(useMixedCaseClassNames);
+                    packageMap.put(packageName, packageClassNameFactory);
+                }
+                else
+                {
+                    // Fall back on the default package class name factory and name.
+                    packageClassNameFactory = defaultPackageClassNameFactory;
+                    newPackageName          = defaultPackageName;
+                }
+            }
+
+            // Come up with class names until we get an original one.
+            String newClassName;
+            do
+            {
+                // Let the factory produce a class name.
+                newClassName = packageClassNameFactory.nextName();
+
+                // We may have to add a package part to the class name.
+                if (newPackageName.length() > 0)
+                {
+                    newClassName =
+                        newPackageName +
+                        ClassConstants.INTERNAL_PACKAGE_SEPARATOR +
+                        newClassName;
+                }
+
+            }
+            while (namesToAvoid.contains(newClassName));
+
+            setNewClassName(programClassFile, newClassName);
+        }
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Assigns a new name to the given class file.
+     * @param classFile the given class file.
+     * @param name      the new name.
+     */
+    static void setNewClassName(ClassFile classFile, String name)
+    {
+        classFile.setVisitorInfo(name);
+    }
+
+
+
+    /**
+     * Retrieves the new name of the given class file.
+     * @param classFile the given class file.
+     * @return the class file's new name, or <code>null</code> if it doesn't
+     *         have one yet.
+     */
+    static String newClassName(ClassFile classFile)
+    {
+        Object visitorInfo = classFile.getVisitorInfo();
+
+        return visitorInfo instanceof String ?
+            (String)visitorInfo :
+            null;
+    }
+}
diff --git a/src/proguard/obfuscate/ClassFileRenamer.java b/src/proguard/obfuscate/ClassFileRenamer.java
new file mode 100644
index 0000000..7e23ca2
--- /dev/null
+++ b/src/proguard/obfuscate/ClassFileRenamer.java
@@ -0,0 +1,716 @@
+/* $Id: ClassFileRenamer.java,v 1.37 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This <code>ClassFileVisitor</code> renames the class names and class member
+ * names of the classes it visits, using names previously determined by the
+ * obfuscator. It can also make package visible classes and class members public,
+ * and it can replace the source file attribute by a given constant string.
+ *
+ * @see ClassFileObfuscator
+ *
+ * @author Eric Lafortune
+ */
+public class ClassFileRenamer
+  implements ClassFileVisitor,
+             MemberInfoVisitor,
+             CpInfoVisitor,
+             AttrInfoVisitor,
+             LocalVariableInfoVisitor,
+             LocalVariableTypeInfoVisitor,
+             AnnotationVisitor,
+             ElementValueVisitor
+{
+    private MyNameAndTypeRenamer nameAndTypeRenamer = new MyNameAndTypeRenamer();
+
+    private boolean openUpPackages;
+    private String  newSourceFileAttribute;
+
+
+    /**
+     * Creates a new ClassFileRenamer.
+     * @param openUpPackages         specifies whether to make package visible
+     *                               classes and class members public.
+     * @param newSourceFileAttribute the new string to be put in the source file
+     *                               attribute (if present) of the visited classes,
+     *                               or <code>null</code> to leave it unchanged.
+     */
+    public ClassFileRenamer(boolean openUpPackages,
+                            String  newSourceFileAttribute)
+    {
+        this.openUpPackages         = openUpPackages;
+        this.newSourceFileAttribute = newSourceFileAttribute;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Rename class members.
+        programClassFile.fieldsAccept(this);
+        programClassFile.methodsAccept(this);
+
+        // Rename NameAndTypeCpInfo type references in the constant pool.
+        programClassFile.constantPoolEntriesAccept(nameAndTypeRenamer);
+
+        // Rename class references and class member references in the constant pool.
+        programClassFile.constantPoolEntriesAccept(this);
+
+        // Make package visible classes public, if specified.
+        if (openUpPackages && isPackageVisible(programClassFile.u2accessFlags))
+        {
+            programClassFile.u2accessFlags = makePublic(programClassFile.u2accessFlags);
+        }
+
+        programClassFile.attributesAccept(this);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile) {}
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        visitMemberInfo(programClassFile, programFieldInfo);
+    }
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        visitMemberInfo(programClassFile, programMethodInfo);
+    }
+
+    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    {
+        String name       = programMemberInfo.getName(programClassFile);
+        String descriptor = programMemberInfo.getDescriptor(programClassFile);
+
+        // The new name is stored with the class member.
+        String newName = MemberInfoObfuscator.newMemberName(programMemberInfo);
+        if (newName != null &&
+            !newName.equals(name))
+        {
+            programMemberInfo.u2nameIndex =
+                createUtf8CpInfo(programClassFile, newName);
+        }
+
+        // Compute the new descriptor.
+        String newDescriptor = newDescriptor(programMemberInfo.getDescriptor(programClassFile),
+                                             programMemberInfo.referencedClassFiles);
+        if (newDescriptor != null)
+        {
+            programMemberInfo.u2descriptorIndex =
+                createUtf8CpInfo(programClassFile, newDescriptor);
+        }
+
+        // Make package visible class members public, if specified.
+        if (openUpPackages && isPackageVisible(programMemberInfo.u2accessFlags))
+        {
+            programMemberInfo.u2accessFlags = makePublic(programMemberInfo.u2accessFlags);
+        }
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+
+
+    /**
+     * This CpInfoVisitor renames all type elements in all NameAndTypeCpInfo
+     * constant pool entries it visits.
+     */
+    private class MyNameAndTypeRenamer
+       implements CpInfoVisitor
+    {
+        // Implementations for CpInfoVisitor.
+
+        public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+        public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+        public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+        public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+        public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
+        public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+        public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
+        public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
+        public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
+        public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
+
+
+        public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo)
+        {
+            // Compute the new descriptor.
+            String newDescriptor = newDescriptor(nameAndTypeCpInfo.getType(classFile),
+                                                 nameAndTypeCpInfo.referencedClassFiles);
+            if (newDescriptor != null)
+            {
+                nameAndTypeCpInfo.u2descriptorIndex =
+                    createUtf8CpInfo((ProgramClassFile)classFile, newDescriptor);
+            }
+        }
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+
+
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
+    {
+        // If the string is being used in a Class.forName construct, the new
+        // class name can be retrieved from the referenced ClassFile.
+        String newClassName = newClassName(stringCpInfo.getString(classFile),
+                                           stringCpInfo.referencedClassFile);
+        if (newClassName != null)
+        {
+            String newExternalClassName = ClassUtil.externalClassName(newClassName);
+
+            // Refer to a new Utf8 entry.
+            stringCpInfo.u2stringIndex =
+                createUtf8CpInfo((ProgramClassFile)classFile, newExternalClassName);
+        }
+    }
+
+
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    {
+        // Compute the new class name (or type).
+        String newClassName = newClassName(classCpInfo.getName(classFile),
+                                           classCpInfo.referencedClassFile);
+        if (newClassName != null)
+        {
+            // Refer to a new Utf8 entry.
+            classCpInfo.u2nameIndex =
+                createUtf8CpInfo((ProgramClassFile)classFile, newClassName);
+        }
+    }
+
+
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    {
+        visitRefCpInfo(classFile, fieldrefCpInfo);
+    }
+
+
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    {
+        visitRefCpInfo(classFile, interfaceMethodrefCpInfo);
+    }
+
+
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    {
+        visitRefCpInfo(classFile, methodrefCpInfo);
+    }
+
+
+    private void visitRefCpInfo(ClassFile classFile, RefCpInfo refCpInfo)
+    {
+        // Compute the new class member name.
+        String newMemberName = newMemberName(refCpInfo.referencedMemberInfo);
+
+        if (newMemberName != null)
+        {
+            // Refer to a new NameAndType entry.
+            refCpInfo.u2nameAndTypeIndex =
+                createNameAndTypeCpInfo((ProgramClassFile)classFile,
+                                        newMemberName,
+                                        refCpInfo.getType(classFile));
+        }
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo) {}
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
+
+
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
+    {
+        // Compute the new class member name.
+        String newMethodName = newMemberName(enclosingMethodAttrInfo.referencedMethodInfo);
+
+        if (newMethodName != null)
+        {
+            // Refer to a new NameAndType entry.
+            enclosingMethodAttrInfo.u2nameAndTypeIndex =
+                createNameAndTypeCpInfo((ProgramClassFile)classFile,
+                                        newMethodName,
+                                        enclosingMethodAttrInfo.getType(classFile));
+        }
+    }
+
+
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
+    {
+        // Rename the types of the local variables.
+        localVariableTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+    }
+
+
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
+    {
+        // Rename the signatures of the local variables.
+        localVariableTypeTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+    }
+
+
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo)
+    {
+        // Rename the source file attribute, if specified.
+        if (newSourceFileAttribute != null)
+        {
+            sourceFileAttrInfo.u2sourceFileIndex =
+                createUtf8CpInfo((ProgramClassFile)classFile, newSourceFileAttribute);
+        }
+    }
+
+
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo)
+    {
+        // Rename the source file attribute, if specified.
+        if (newSourceFileAttribute != null)
+        {
+            sourceDirAttrInfo.u2sourceDirIndex =
+                createUtf8CpInfo((ProgramClassFile)classFile, newSourceFileAttribute);
+        }
+    }
+
+
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
+    {
+        // Compute the new signature.
+        String newSignature = newDescriptor(classFile.getCpString(signatureAttrInfo.u2signatureIndex),
+                                            signatureAttrInfo.referencedClassFiles);
+        if (newSignature != null)
+        {
+            signatureAttrInfo.u2signatureIndex =
+                createUtf8CpInfo((ProgramClassFile)classFile, newSignature);
+        }
+    }
+
+
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
+    {
+        // Rename the annotations.
+        runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
+    {
+        // Rename the annotations.
+        runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
+    {
+        // Rename the annotations.
+        runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
+    {
+        // Rename the annotations.
+        runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
+    {
+        // Rename the annotation.
+        annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
+    }
+
+
+    // Implementations for LocalVariableInfoVisitor.
+
+    public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo)
+    {
+        // Compute the new descriptor.
+        String newDescriptor = newClassName(classFile.getCpString(localVariableInfo.u2descriptorIndex),
+                                            localVariableInfo.referencedClassFile);
+        if (newDescriptor != null)
+        {
+            // Refer to a new Utf8 entry.
+            localVariableInfo.u2descriptorIndex =
+                createUtf8CpInfo((ProgramClassFile)classFile, newDescriptor);
+        }
+    }
+
+
+    // Implementations for LocalVariableTypeInfoVisitor.
+
+    public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo)
+    {
+        // Compute the new signature.
+        String newSignature = newDescriptor(classFile.getCpString(localVariableTypeInfo.u2signatureIndex),
+                                            localVariableTypeInfo.referencedClassFiles);
+        if (newSignature != null)
+        {
+            localVariableTypeInfo.u2signatureIndex =
+                createUtf8CpInfo((ProgramClassFile)classFile, newSignature);
+        }
+    }
+
+
+    // Implementations for AnnotationVisitor.
+
+    public void visitAnnotation(ClassFile classFile, Annotation annotation)
+    {
+        // Compute the new type name.
+        String newTypeName = newDescriptor(classFile.getCpString(annotation.u2typeIndex),
+                                           annotation.referencedClassFiles);
+        if (newTypeName != null)
+        {
+            // Refer to a new Utf8 entry.
+            annotation.u2typeIndex =
+                createUtf8CpInfo((ProgramClassFile)classFile, newTypeName);
+        }
+
+        // Rename the element values.
+        annotation.elementValuesAccept(classFile, this);
+    }
+
+
+    // Implementations for ElementValueVisitor.
+
+    public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue)
+    {
+        renameElementValue(classFile, constantElementValue);
+    }
+
+
+    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+    {
+        renameElementValue(classFile, enumConstantElementValue);
+
+        // Compute the new type name.
+        String newTypeName = newDescriptor(classFile.getCpString(enumConstantElementValue.u2typeNameIndex),
+                                           enumConstantElementValue.referencedClassFiles);
+        if (newTypeName != null)
+        {
+            // Refer to a new Utf8 entry.
+            enumConstantElementValue.u2typeNameIndex =
+                createUtf8CpInfo((ProgramClassFile)classFile, newTypeName);
+        }
+    }
+
+
+    public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
+    {
+        renameElementValue(classFile, classElementValue);
+
+        // Compute the new class name.
+        String newClassName = newDescriptor(classFile.getCpString(classElementValue.u2classInfoIndex),
+                                            classElementValue.referencedClassFiles);
+
+        if (newClassName != null)
+        {
+            // Refer to a new Utf8 entry.
+            classElementValue.u2classInfoIndex =
+                createUtf8CpInfo((ProgramClassFile)classFile, newClassName);
+        }
+    }
+
+
+    public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue)
+    {
+        renameElementValue(classFile, annotationElementValue);
+
+        // Rename the annotation.
+        annotationElementValue.annotationAccept(classFile, this);
+    }
+
+
+    public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
+    {
+        renameElementValue(classFile, arrayElementValue);
+
+        // Rename the element values.
+        arrayElementValue.elementValuesAccept(classFile, annotation, this);
+    }
+
+
+    /**
+     * Renames the method reference of the element value, if any.
+     */
+    public void renameElementValue(ClassFile classFile, ElementValue elementValue)
+    {
+        // Compute the new class member name.
+        String newMethodName = newMemberName(elementValue.referencedMethodInfo);
+
+        if (newMethodName != null)
+        {
+            // Refer to a new NameAndType entry.
+            elementValue.u2elementName =
+                createUtf8CpInfo((ProgramClassFile)classFile, newMethodName);
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Finds or creates a NameAndTypeCpInfo constant pool entry with the given
+     * name and type, in the given class file.
+     * @return the constant pool index of the NameAndTypeCpInfo.
+     */
+    private int createNameAndTypeCpInfo(ProgramClassFile programClassFile,
+                                        String           name,
+                                        String           type)
+    {
+        CpInfo[] constantPool        = programClassFile.constantPool;
+        int      u2constantPoolCount = programClassFile.u2constantPoolCount;
+
+        // Pick up the right list of referenced class files, in case we need to
+        // create a new NameAndTypeCpInfo.
+        ClassFile[] referencedClassFiles = null;
+
+        // Check if there is a NameAndTypeCpInfo with the given name and type already.
+        for (int index = 1; index < u2constantPoolCount; index++)
+        {
+            CpInfo cpInfo = constantPool[index];
+
+            if (cpInfo != null &&
+                cpInfo.getTag() == ClassConstants.CONSTANT_NameAndType)
+            {
+                NameAndTypeCpInfo nameAndTypeCpInfo = (NameAndTypeCpInfo)cpInfo;
+                if (nameAndTypeCpInfo.getType(programClassFile).equals(type))
+                {
+                    if (nameAndTypeCpInfo.getName(programClassFile).equals(name))
+                    {
+                        return index;
+                    }
+
+                    referencedClassFiles = nameAndTypeCpInfo.referencedClassFiles;
+                }
+            }
+        }
+
+        int u2nameIndex       = createUtf8CpInfo(programClassFile, name);
+        int u2descriptorIndex = createUtf8CpInfo(programClassFile, type);
+
+        return addCpInfo(programClassFile, new NameAndTypeCpInfo(u2nameIndex,
+                                                                 u2descriptorIndex,
+                                                                 referencedClassFiles));
+    }
+
+
+    /**
+     * Finds or creates an Utf8CpInfo constant pool entry for the given string,
+     * in the given class file.
+     * @return the constant pool index of the Utf8CpInfo.
+     */
+    private int createUtf8CpInfo(ProgramClassFile programClassFile, String string)
+    {
+        CpInfo[] constantPool        = programClassFile.constantPool;
+        int      u2constantPoolCount = programClassFile.u2constantPoolCount;
+
+        // Check if there is a Utf8CpInfo with the given string already.
+        for (int index = 1; index < u2constantPoolCount; index++)
+        {
+            CpInfo cpInfo = constantPool[index];
+
+            if (cpInfo != null &&
+                cpInfo.getTag() == ClassConstants.CONSTANT_Utf8)
+            {
+                Utf8CpInfo utf8CpInfo = (Utf8CpInfo)cpInfo;
+                if (utf8CpInfo.getString().equals(string))
+                {
+                    return index;
+                }
+            }
+        }
+
+        return addCpInfo(programClassFile, new Utf8CpInfo(string));
+    }
+
+
+    /**
+     * Adds a given constant pool entry to the end of the constant pool.
+     * @return the constant pool index for the added entry.
+     */
+    private int addCpInfo(ProgramClassFile programClassFile, CpInfo cpInfo)
+    {
+        CpInfo[] constantPool        = programClassFile.constantPool;
+        int      u2constantPoolCount = programClassFile.u2constantPoolCount;
+
+        // Make sure there is enough space for another constant pool entry.
+        if (u2constantPoolCount == constantPool.length)
+        {
+            programClassFile.constantPool = new CpInfo[u2constantPoolCount+1];
+            System.arraycopy(constantPool, 0,
+                             programClassFile.constantPool, 0,
+                             u2constantPoolCount);
+            constantPool = programClassFile.constantPool;
+
+        }
+
+        // Create a new Utf8CpInfo for the given string.
+        constantPool[programClassFile.u2constantPoolCount++] = cpInfo;
+
+        return u2constantPoolCount;
+    }
+
+
+    /**
+     * Returns the new descriptor based on the given descriptor and the new
+     * names of the given referenced class files.
+     */
+    private String newDescriptor(String      descriptor,
+                                 ClassFile[] referencedClassFiles)
+    {
+        // If there are no referenced classes, the descriptor doesn't change.
+        if (referencedClassFiles == null)
+        {
+            return null;
+        }
+
+        // Unravel and reconstruct the class elements of the descriptor.
+        DescriptorClassEnumeration descriptorClassEnumeration =
+            new DescriptorClassEnumeration(descriptor);
+
+        String newDescriptor = descriptorClassEnumeration.nextFluff();
+
+        int index = 0;
+        while (descriptorClassEnumeration.hasMoreClassNames())
+        {
+            String className = descriptorClassEnumeration.nextClassName();
+            String fluff     = descriptorClassEnumeration.nextFluff();
+
+            String newClassName = newClassName(className,
+                                               referencedClassFiles[index++]);
+
+            // Fall back on the original class name if there is no new name.
+            if (newClassName == null)
+            {
+                newClassName = className;
+            }
+
+            newDescriptor = newDescriptor + newClassName + fluff;
+        }
+
+        // If the descriptor hasn't changed after all, just return null.
+        if (descriptor.equals(newDescriptor))
+        {
+            return null;
+        }
+
+        return newDescriptor;
+    }
+
+
+    /**
+     * Returns the new class name based on the given class name and the new
+     * name of the given referenced class file. Class names of array types
+     * are handled properly.
+     */
+    private String newClassName(String    className,
+                                ClassFile referencedClassFile)
+    {
+        // If there is no referenced class, the descriptor doesn't change.
+        if (referencedClassFile == null)
+        {
+            return null;
+        }
+
+        String newClassName =
+            ClassFileObfuscator.newClassName(referencedClassFile);
+
+        // If there is no new class name, the descriptor doesn't change.
+        if (newClassName == null)
+        {
+            return null;
+        }
+
+        // Is it an array type?
+        if (className.charAt(0) == ClassConstants.INTERNAL_TYPE_ARRAY)
+        {
+            // Add the array prefixes and suffix "[L...;".
+            newClassName =
+                 className.substring(0, className.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_START)+1) +
+                 newClassName +
+                 ClassConstants.INTERNAL_TYPE_CLASS_END;
+        }
+
+        return newClassName;
+    }
+
+
+    /**
+     * Returns the new class member name based on the given referenced class
+     * member.
+     */
+    private String newMemberName(MemberInfo referencedMemberInfo)
+    {
+        if (referencedMemberInfo == null)
+        {
+            return null;
+        }
+
+        return MemberInfoObfuscator.newMemberName(referencedMemberInfo);
+    }
+
+
+    /**
+     * Returns whether the given access flags specify a package visible class
+     * or class member (including public or protected access).
+     */
+    private boolean isPackageVisible(int accessFlags)
+    {
+        return AccessUtil.accessLevel(accessFlags) >= AccessUtil.PACKAGE_VISIBLE;
+    }
+
+
+    /**
+     * Returns the given access flags, modified such that the class or class
+     * member becomes public.
+     */
+    private int makePublic(int accessFlags)
+    {
+        return AccessUtil.replaceAccessFlags(accessFlags,
+                                             ClassConstants.INTERNAL_ACC_PUBLIC);
+    }
+}
diff --git a/src/proguard/obfuscate/MappingKeeper.java b/src/proguard/obfuscate/MappingKeeper.java
new file mode 100644
index 0000000..69494a9
--- /dev/null
+++ b/src/proguard/obfuscate/MappingKeeper.java
@@ -0,0 +1,119 @@
+/* $Id: MappingKeeper.java,v 1.8 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.util.ClassUtil;
+
+
+/**
+ * This MappingKeeper applies the mappings that it receives to its class pool,
+ * so these mappings are ensured in a subsequent obfuscation step.
+ *
+ * @author Eric Lafortune
+ */
+public class MappingKeeper implements MappingProcessor
+{
+    private ClassPool classPool;
+
+    // A field acting as a parameter.
+    private ClassFile classFile;
+
+
+    /**
+     * Creates a new MappingKeeper.
+     * @param classPool the class pool in which class names and class member names
+     *                  have to be mapped.
+     */
+    public MappingKeeper(ClassPool classPool)
+    {
+        this.classPool = classPool;
+    }
+
+
+    // Implementations for MappingProcessor.
+
+    public boolean processClassFileMapping(String className,
+                                           String newClassName)
+    {
+        // Find the class.
+        String name = ClassUtil.internalClassName(className);
+
+        classFile = classPool.getClass(name);
+        if (classFile != null)
+        {
+            // Make sure the mapping name will be kept.
+            String newName = ClassUtil.internalClassName(newClassName);
+
+            ClassFileObfuscator.setNewClassName(classFile, newName);
+
+            // The class members have to be kept as well.
+            return true;
+        }
+
+        return false;
+    }
+
+
+    public void processFieldMapping(String className,
+                                    String fieldType,
+                                    String fieldName,
+                                    String newFieldName)
+    {
+        if (classFile != null)
+        {
+            // Find the field.
+            String name       = fieldName;
+            String descriptor = ClassUtil.internalType(fieldType);
+
+            FieldInfo fieldInfo = classFile.findField(name, descriptor);
+            if (fieldInfo != null)
+            {
+                // Make sure the mapping name will be kept.
+                MemberInfoObfuscator.setNewMemberName(fieldInfo, newFieldName);
+            }
+        }
+    }
+
+
+    public void processMethodMapping(String className,
+                                     int    firstLineNumber,
+                                     int    lastLineNumber,
+                                     String methodReturnType,
+                                     String methodNameAndArguments,
+                                     String newMethodName)
+    {
+        if (classFile != null)
+        {
+            // Find the method.
+            String name       = ClassUtil.externalMethodName(methodNameAndArguments);
+            String descriptor = ClassUtil.internalMethodDescriptor(methodReturnType,
+                                                                   methodNameAndArguments);
+
+            MethodInfo methodInfo = classFile.findMethod(name, descriptor);
+            if (methodInfo != null)
+            {
+                // Make sure the mapping name will be kept.
+                MemberInfoObfuscator.setNewMemberName(methodInfo, newMethodName);
+            }
+        }
+    }
+}
diff --git a/src/proguard/obfuscate/MappingPrinter.java b/src/proguard/obfuscate/MappingPrinter.java
new file mode 100644
index 0000000..dd38362
--- /dev/null
+++ b/src/proguard/obfuscate/MappingPrinter.java
@@ -0,0 +1,170 @@
+/* $Id: MappingPrinter.java,v 1.15 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+
+
+/**
+ * This ClassFileVisitor prints out the renamed class files and class members with
+ * their old names and new names.
+ *
+ * @see ClassFileRenamer
+ *
+ * @author Eric Lafortune
+ */
+public class MappingPrinter
+  implements ClassFileVisitor,
+             MemberInfoVisitor
+{
+    private PrintStream ps;
+
+    // A field to remember the class name, if a header is needed for class members.
+    private String className;
+
+
+    /**
+     * Creates a new MappingPrinter that prints to <code>System.out</code>.
+     */
+    public MappingPrinter()
+    {
+        this(System.out);
+    }
+
+
+    /**
+     * Creates a new MappingPrinter that prints to the given stream.
+     * @param printStream the stream to which to print
+     */
+    public MappingPrinter(PrintStream printStream)
+    {
+        this.ps = printStream;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        className = programClassFile.getName();
+
+        String newClassName = ClassFileObfuscator.newClassName(programClassFile);
+
+        if (newClassName != null)
+        {
+            ps.println(ClassUtil.externalClassName(className) +
+                       " -> " +
+                       ClassUtil.externalClassName(newClassName) +
+                       ":");
+
+            className = null;
+        }
+
+        // Print out the class members.
+        programClassFile.fieldsAccept(this);
+        programClassFile.methodsAccept(this);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        String newMemberName = MemberInfoObfuscator.newMemberName(programFieldInfo);
+
+        if (newMemberName != null)
+        {
+            printClassNameHeader();
+
+            ps.println("    " +
+                       lineNumberRange(programClassFile, programFieldInfo) +
+                       ClassUtil.externalFullFieldDescription(
+                           0,
+                           programFieldInfo.getName(programClassFile),
+                           programFieldInfo.getDescriptor(programClassFile)) +
+                       " -> " +
+                       newMemberName);
+        }
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+      String newMemberName = MemberInfoObfuscator.newMemberName(programMethodInfo);
+
+      if (newMemberName != null)
+        {
+            printClassNameHeader();
+
+            ps.println("    " +
+                       lineNumberRange(programClassFile, programMethodInfo) +
+                       ClassUtil.externalFullMethodDescription(
+                       programClassFile.getName(),
+                       0,
+                       programMethodInfo.getName(programClassFile),
+                       programMethodInfo.getDescriptor(programClassFile)) +
+                       " -> " +
+                       newMemberName);
+        }
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+
+
+    // Small utility methods.
+
+    /**
+     * Prints the class name field. The field is then cleared, so it is not
+     * printed again.
+     */
+    private void printClassNameHeader()
+    {
+        if (className != null)
+        {
+            ps.println(ClassUtil.externalClassName(className) + ":");
+            className = null;
+        }
+    }
+
+
+    /**
+     * Returns the line number range of the given class member, followed by a
+     * colon, or just an empty String if no range is available.
+     */
+    private static String lineNumberRange(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    {
+        String range = programMemberInfo.getLineNumberRange(programClassFile);
+        return range != null ?
+            (range + ":") :
+            "";
+    }
+}
diff --git a/src/proguard/obfuscate/MappingProcessor.java b/src/proguard/obfuscate/MappingProcessor.java
new file mode 100644
index 0000000..a37df97
--- /dev/null
+++ b/src/proguard/obfuscate/MappingProcessor.java
@@ -0,0 +1,78 @@
+/* $Id: MappingProcessor.java,v 1.4 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+
+/**
+ * This interface specifies methods to process name mappings between original
+ * class files and their obfuscated versions. The mappings are typically read
+ * from a mapping file.
+ *
+ * @see MappingReader
+ *
+ * @author Eric Lafortune
+ */
+public interface MappingProcessor
+{
+    /**
+     * Processes the given class name mapping.
+     *
+     * @param className    the original class name.
+     * @param newClassName the new class name.
+     * @return whether the processor is interested in receiving mappings of the
+     *         class members of this class.
+     */
+    public boolean processClassFileMapping(String className,
+                                           String newClassName);
+
+    /**
+     * Processes the given field name mapping.
+     *
+     * @param className    the original class name.
+     * @param fieldType    the original external field type.
+     * @param fieldName    the original field name.
+     * @param newFieldName the new field name.
+     */
+    public void processFieldMapping(String className,
+                                    String fieldType,
+                                    String fieldName,
+                                    String newFieldName);
+
+    /**
+     * Processes the given method name mapping.
+     *
+     * @param className              the original class name.
+     * @param firstLineNumber        the first line number of the method, or
+     *                               0 if it is not known.
+     * @param lastLineNumber         the last line number of the method, or
+     *                               0 if it is not known.
+     * @param methodReturnType       the original external method return type.
+     * @param methodNameAndArguments the original external method name and
+     *                               arguments.
+     * @param newMethodName          the new method name.
+     */
+    public void processMethodMapping(String className,
+                                     int    firstLineNumber,
+                                     int    lastLineNumber,
+                                     String methodReturnType,
+                                     String methodNameAndArguments,
+                                     String newMethodName);
+}
diff --git a/src/proguard/obfuscate/MappingReader.java b/src/proguard/obfuscate/MappingReader.java
new file mode 100644
index 0000000..bad1218
--- /dev/null
+++ b/src/proguard/obfuscate/MappingReader.java
@@ -0,0 +1,195 @@
+/* $Id: MappingReader.java,v 1.8 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import java.io.*;
+
+
+/**
+ * This class can parse mapping files and invoke a processor for each of the
+ * mapping entries.
+ *
+ * @author Eric Lafortune
+ */
+public class MappingReader
+{
+    private String mappingFileName;
+
+
+    public MappingReader(String mappingFileName)
+    {
+        this.mappingFileName = mappingFileName;
+    }
+
+
+    /**
+     * Reads the mapping file, presenting all of the encountered mapping entries
+     * to the given processor.
+     */
+    public void pump(MappingProcessor mappingProcessor) throws IOException
+    {
+        LineNumberReader reader = null;
+
+        try
+        {
+            reader = new LineNumberReader(
+                     new BufferedReader(
+                     new FileReader(mappingFileName)));
+
+            String className = null;
+
+            // Read the subsequent class mappings and class member mappings.
+            while (true)
+            {
+                String line = reader.readLine();
+
+                if (line == null)
+                {
+                    break;
+                }
+
+                // The distinction between a class file mapping and a class
+                // member mapping is the initial whitespace.
+                if (!line.startsWith("    "))
+                {
+                    // Process the class file mapping and remember the class's
+                    // old name.
+                    className = processClassFileMapping(line, mappingProcessor);
+                }
+                else if (className != null)
+                {
+                    // Process the class member mapping, in the context of the
+                    // current old class name.
+                    processClassMemberMapping(className, line, mappingProcessor);
+                }
+            }
+        }
+        catch (IOException ex)
+        {
+            throw new IOException("Can't process mapping file (" + ex.getMessage() + ")");
+        }
+        finally
+        {
+            if (reader != null)
+            {
+                try
+                {
+                    reader.close();
+                }
+                catch (IOException ex)
+                {
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Parses the given line with a class file mapping and processes the
+     * results with the given mapping processor. Returns the old class name,
+     * or null if any subsequent class member lines can be ignored.
+     */
+    private String processClassFileMapping(String           line,
+                                           MappingProcessor mappingProcessor)
+    {
+        // See if we can parse "___ -> ___:", containing the original
+        // class name and the new class name.
+
+        line = line.trim();
+
+        int arrowIndex = line.indexOf("->");
+        if (arrowIndex < 0)
+        {
+            return null;
+        }
+
+        int colonIndex = line.indexOf(':', arrowIndex + 2);
+        if (colonIndex < 0)
+        {
+            return null;
+        }
+
+        // Extract the elements.
+        String className    = line.substring(0, arrowIndex).trim();
+        String newClassName = line.substring(arrowIndex + 2, colonIndex).trim();
+
+        // Process this class name mapping.
+        boolean interested = mappingProcessor.processClassFileMapping(className, newClassName);
+
+        return interested ? className : null;
+    }
+
+
+    /**
+     * Parses the given line with a class member mapping and processes the
+     * results with the given mapping processor.
+     */
+    private void processClassMemberMapping(String           className,
+                                           String           line,
+                                           MappingProcessor mappingProcessor)
+    {
+        // See if we can parse "    ___:___:___ ___ -> ___",
+        // containing the optional line numbers, the return type, the original
+        // field/method name (including arguments), and the new method name.
+
+        line = line.trim();
+
+        int colonIndex1 = line.indexOf(':');
+        int colonIndex2 = line.indexOf(':', colonIndex1 + 1);
+
+        int spaceIndex = line.indexOf(' ', colonIndex2 + 1);
+        if (spaceIndex < 0)
+        {
+            return;
+        }
+
+        int arrowIndex = line.indexOf("->", spaceIndex + 1);
+        if (arrowIndex < 1)
+        {
+            return;
+        }
+
+        int firstLineNumber = colonIndex1 < 0 ? 0 :
+            Integer.parseInt(line.substring(0, colonIndex1).trim());
+
+        int lastLineNumber = colonIndex1 < 0 ||
+                             colonIndex2 < 0 ? 0 :
+            Integer.parseInt(line.substring(colonIndex1 + 1, colonIndex2).trim());
+
+        // Extract the elements.
+        String type    = line.substring(colonIndex2 + 1, spaceIndex).trim();
+        String name    = line.substring(spaceIndex + 1, arrowIndex).trim();
+        String newName = line.substring(arrowIndex + 2).trim();
+
+        // Process this class member mapping.
+        if (type.length() > 0 && name.length() > 0)
+        {
+            if (name.charAt(name.length() - 1) != ')')
+            {
+                mappingProcessor.processFieldMapping(className, type, name, newName);
+            }
+            else
+            {
+                mappingProcessor.processMethodMapping(className, firstLineNumber, lastLineNumber, type, name, newName);
+            }
+        }
+    }
+}
diff --git a/src/proguard/obfuscate/MemberInfoLinker.java b/src/proguard/obfuscate/MemberInfoLinker.java
new file mode 100644
index 0000000..6afe6a0
--- /dev/null
+++ b/src/proguard/obfuscate/MemberInfoLinker.java
@@ -0,0 +1,185 @@
+/* $Id: MemberInfoLinker.java,v 1.9 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.*;
+
+import java.util.*;
+
+
+/**
+ * This ClassFileVisitor links all methods that should get the same names
+ * in the name spaces of all visited class files. A class file's name space
+ * encompasses all of its subclasses and interfaces. It is typically a class file
+ * that is not being subclassed. Chains of links that have been created in
+ * previous invocations are merged with new chains of links, in order to create
+ * a consistent set of chains. Class initialization methods and constructors are
+ * ignored.
+ *
+ * @see MemberInfoObfuscator
+ *
+ * @author Eric Lafortune
+ */
+public class MemberInfoLinker
+  implements ClassFileVisitor,
+             MemberInfoVisitor
+{
+    // An object that is reset and reused every time.
+    // The map: [class member name+descriptor - class member info]
+    private final Map methodInfoMap = new HashMap();
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Collect all members in this class's name space.
+        programClassFile.hierarchyAccept(true, true, true, false,
+                                         new AllMemberInfoVisitor(this));
+
+        // Clean up for obfuscation of the next name space.
+        methodInfoMap.clear();
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        visitMethodInfo(programClassFile, programMethodInfo);
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    {
+    }
+
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        visitMethodInfo(libraryClassFile, libraryMethodInfo);
+    }
+
+
+    /**
+     * Links the given method into the chains of links. Class initialization
+     * methods and constructors are ignored.
+     * @param classFile  the class file of the given method.
+     * @param methodInfo the method to be linked.
+     */
+    private void visitMethodInfo(ClassFile classFile, MethodInfo methodInfo)
+    {
+        // Private methods don't have to be linked.
+        if ((methodInfo.getAccessFlags() & ClassConstants.INTERNAL_ACC_PRIVATE) != 0)
+        {
+            return;
+        }
+
+        // Get the method's original name and descriptor.
+        String name       = methodInfo.getName(classFile);
+        String descriptor = methodInfo.getDescriptor(classFile);
+
+        // Special cases: <clinit> and <init> are always kept unchanged.
+        // We can ignore them here.
+        if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) ||
+            name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
+        {
+            return;
+        }
+
+        // Get the last method in the chain.
+        MemberInfo thisLastMemberInfo = lastMemberInfo(methodInfo);
+
+        // See if we've already come across a method with the same name and
+        // descriptor.
+        String key = name + descriptor;
+        MethodInfo otherMethodInfo = (MethodInfo)methodInfoMap.get(key);
+
+        if (otherMethodInfo == null)
+        {
+            // Store the new class method info in the map.
+            methodInfoMap.put(key, thisLastMemberInfo);
+        }
+        else
+        {
+            // Get the last method in the other chain.
+            MemberInfo otherLastMemberInfo = lastMemberInfo(otherMethodInfo);
+
+            // Check if both link chains aren't already ending in the same element.
+            if (thisLastMemberInfo != otherLastMemberInfo)
+            {
+                // Merge the two chains, making sure LibraryMethodInfo elements,
+                // if any, are at the end of the resulting chain.
+                if (thisLastMemberInfo instanceof LibraryMethodInfo)
+                {
+                    // This class method chain ends with a library class method.
+                    // Link this chain to the end of the other one.
+                    otherLastMemberInfo.setVisitorInfo(thisLastMemberInfo);
+                }
+                /* We can skip this test and go straight to the final case.
+                else if (otherLastVisitorAccepter instanceof LibraryMethodInfo)
+                {
+                    // The other method chain ends with a library class method.
+                    // Link the other chain to the end of this one.
+                    thisLastVisitorAccepter.setVisitorInfo(otherLastVisitorAccepter);
+                }
+                */
+                else
+                {
+                    // We have two non-library methods. Link their chains
+                    // one way or another.
+                    thisLastMemberInfo.setVisitorInfo(otherLastMemberInfo);
+                }
+            }
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Finds the last class member in the linked list of class members.
+     * @param memberInfo the given class member.
+     * @return the last class member in the linked list.
+     */
+    static MemberInfo lastMemberInfo(MemberInfo memberInfo)
+    {
+        VisitorAccepter lastVisitorAccepter = memberInfo;
+        while (lastVisitorAccepter.getVisitorInfo() != null &&
+               lastVisitorAccepter.getVisitorInfo() instanceof VisitorAccepter)
+        {
+            lastVisitorAccepter = (VisitorAccepter)lastVisitorAccepter.getVisitorInfo();
+        }
+
+        return (MemberInfo)lastVisitorAccepter;
+    }
+}
diff --git a/src/proguard/obfuscate/MemberInfoObfuscator.java b/src/proguard/obfuscate/MemberInfoObfuscator.java
new file mode 100644
index 0000000..94d3171
--- /dev/null
+++ b/src/proguard/obfuscate/MemberInfoObfuscator.java
@@ -0,0 +1,404 @@
+/* $Id: MemberInfoObfuscator.java,v 1.12 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.*;
+
+import java.io.IOException;
+import java.util.*;
+
+
+/**
+ * This ClassFileVisitor obfuscates all class members in the name spaces of all
+ * visited class file. The class members must have been linked before applying this
+ * visitor. The class file is typically a class file that is not being subclassed.
+ *
+ * @see MemberInfoLinker
+ *
+ * @author Eric Lafortune
+ */
+public class MemberInfoObfuscator implements ClassFileVisitor
+{
+    private static final char UNIQUE_SUFFIX = '_';
+
+    private boolean allowAggressiveOverloading;
+
+    private NameFactory nameFactory       = new SimpleNameFactory();
+    private NameFactory uniqueNameFactory = new SimpleNameFactory();
+
+    // Some objects that are reset and reused every time.
+
+    // The main maps: [class member descriptor - new name - name]
+    private final Map nonPrivateDescriptorMap = new HashMap();
+    private final Map privateDescriptorMap    = new HashMap();
+
+
+
+    /**
+     * Creates a new MemberObfuscator.
+     * @param allowAggressiveOverloading a flag that specifies whether class
+     *                                   members can be overloaded aggressively.
+     * @param obfuscationDictionary      the optional name of a file from which
+     *                                   obfuscated method names can be read.
+     */
+    public MemberInfoObfuscator(boolean allowAggressiveOverloading,
+                                String  obfuscationDictionary)
+    throws IOException
+    {
+        this.allowAggressiveOverloading = allowAggressiveOverloading;
+
+        // Get names from the obfuscation dictionary, if specified.
+        if (obfuscationDictionary != null)
+        {
+            nameFactory = new ReadNameFactory(obfuscationDictionary, nameFactory);
+        }
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Collect all preset new member names in this class's name space.
+        // This actually includes preset new private member names.
+        programClassFile.hierarchyAccept(true, true, true, false,
+                                         new AllMemberInfoVisitor(
+                                         new MyNewNameCollector(nonPrivateDescriptorMap)));
+
+        // Assign new names to all non-private members in this class's name space.
+        programClassFile.hierarchyAccept(true, true, true, false,
+                                         new MyNonPrivateMemberInfoObfuscator());
+
+        // Assign new names to all private members in this class's name space.
+        programClassFile.hierarchyAccept(true, true, true, false,
+                                         new MyPrivateMemberInfoObfuscator());
+
+        // Clean up for obfuscation of the next name space.
+        nonPrivateDescriptorMap.clear();
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+    }
+
+
+    private class MyNonPrivateMemberInfoObfuscator implements ClassFileVisitor
+    {
+        // Implementations for ClassFileVisitor.
+
+        public void visitProgramClassFile(ProgramClassFile programClassFile)
+        {
+            MemberInfoAccessFilter nonPrivateNewNameAssigner =
+                new MemberInfoAccessFilter(
+                0,
+                ClassConstants.INTERNAL_ACC_PRIVATE,
+                new MyNewNameAssigner(nonPrivateDescriptorMap));
+
+            programClassFile.fieldsAccept(nonPrivateNewNameAssigner);
+            programClassFile.methodsAccept(nonPrivateNewNameAssigner);
+        }
+
+
+        public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+        {
+        }
+    }
+
+
+    private class MyPrivateMemberInfoObfuscator implements ClassFileVisitor
+    {
+        // Implementations for ClassFileVisitor.
+
+        public void visitProgramClassFile(ProgramClassFile programClassFile)
+        {
+            MemberInfoAccessFilter privateNewNameAssigner =
+                new MemberInfoAccessFilter(
+                ClassConstants.INTERNAL_ACC_PRIVATE,
+                0,
+                new MyNewNameAssigner(privateDescriptorMap, nonPrivateDescriptorMap));
+
+            programClassFile.fieldsAccept(privateNewNameAssigner);
+            programClassFile.methodsAccept(privateNewNameAssigner);
+
+            // Clean up for obfuscation of the next class.
+            privateDescriptorMap.clear();
+        }
+
+
+        public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+        {
+        }
+    }
+
+
+    private class MyNewNameCollector implements MemberInfoVisitor
+    {
+        private final Map descriptorMap;
+
+
+        public MyNewNameCollector(Map descriptorMap)
+        {
+            this.descriptorMap = descriptorMap;
+        }
+
+
+        // Implementations for MemberInfoVisitor.
+
+        public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+        {
+            visitMemberInfo(programClassFile, programFieldInfo);
+        }
+
+
+        public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+        {
+            visitMemberInfo(programClassFile, programMethodInfo);
+        }
+
+
+        public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+        {
+            // Make sure the library field keeps its name.
+            String name = libraryFieldInfo.getName(libraryClassFile);
+            setNewMemberName(libraryFieldInfo, name);
+
+            visitMemberInfo(libraryClassFile, libraryFieldInfo);
+        }
+
+
+        public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+        {
+            // Make sure the library method keeps its name.
+            String name = libraryMethodInfo.getName(libraryClassFile);
+            setNewMemberName(libraryMethodInfo, name);
+
+            visitMemberInfo(libraryClassFile, libraryMethodInfo);
+        }
+
+
+        /**
+         * Inserts the new name of the given class member into the main map.
+         * @param classFile  the class file of the given member.
+         * @param memberInfo the class member to be linked.
+         */
+        private void visitMemberInfo(ClassFile classFile, MemberInfo memberInfo)
+        {
+            // Special cases: <clinit> and <init> are always kept unchanged.
+            // We can ignore them here.
+            String name = memberInfo.getName(classFile);
+            if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) ||
+                name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
+            {
+                return;
+            }
+
+            // Get the member's new name.
+            String newName = newMemberName(memberInfo);
+
+            // Remember it, if it was set.
+            if (newName != null)
+            {
+                // Get the member's descriptor.
+                String descriptor = memberInfo.getDescriptor(classFile);
+
+                // Check whether we're allowed to do aggressive overloading
+                if (!allowAggressiveOverloading)
+                {
+                    // Trim the return argument from the descriptor if not.
+                    // Works for fields and methods alike.
+                    descriptor = descriptor.substring(0, descriptor.indexOf(')')+1);
+                }
+
+                // Put the [descriptor - new name] in the map,
+                // creating a new set of new names if necessary.
+                Map newNameMap = retrieveNameMap(descriptorMap, descriptor);
+
+                // Is the other original name different from this original
+                // name?
+                String otherName = (String)newNameMap.get(newName);
+                if (otherName != null &&
+                    !otherName.equals(name))
+                {
+                    // There's a conflict! A member (with a given old name) in a
+                    // first namespace has received the same new name as this
+                    // member (with a different old name) in a second name space,
+                    // and now these two have to live together in this name space.
+                    // Assign a truly unique new name to this member.
+                    setNewMemberName(memberInfo, uniqueNameFactory.nextName() + UNIQUE_SUFFIX);
+                }
+                else
+                {
+                    // Remember not to use the new name again in this name space.
+                    newNameMap.put(newName, name);
+                }
+            }
+        }
+    }
+
+
+    private class MyNewNameAssigner implements MemberInfoVisitor
+    {
+        private final Map descriptorMap;
+        private final Map secondaryDescriptorMap;
+
+
+        public MyNewNameAssigner(Map descriptorMap)
+        {
+            this(descriptorMap, null);
+        }
+
+
+        public MyNewNameAssigner(Map descriptorMap,
+                                 Map secondaryDescriptorMap)
+        {
+            this.descriptorMap          = descriptorMap;
+            this.secondaryDescriptorMap = secondaryDescriptorMap;
+        }
+
+
+        // Implementations for MemberInfoVisitor.
+
+        public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+        {
+            visitMemberInfo(programClassFile, programFieldInfo);
+        }
+
+
+        public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+        {
+            visitMemberInfo(programClassFile, programMethodInfo);
+        }
+
+
+        public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+        public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+
+
+        /**
+         * Inserts the given class member into the main map.
+         * @param classFile  the class file of the given member.
+         * @param memberInfo the class member to be linked.
+         */
+        private void visitMemberInfo(ClassFile classFile, MemberInfo memberInfo)
+        {
+            // Special cases: <clinit> and <init> are always kept unchanged.
+            // We can ignore them here.
+            String name = memberInfo.getName(classFile);
+            if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) ||
+                name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
+            {
+                return;
+            }
+
+            // Get the member's new name.
+            String newName = newMemberName(memberInfo);
+
+            // Assign a new one, if necessary.
+            if (newName == null)
+            {
+                // Get the member's descriptor.
+                String descriptor = memberInfo.getDescriptor(classFile);
+
+                // Check whether we're allowed to do aggressive overloading
+                if (!allowAggressiveOverloading)
+                {
+                    // Trim the return argument from the descriptor if not.
+                    // Works for fields and methods alike.
+                    descriptor = descriptor.substring(0, descriptor.indexOf(')')+1);
+                }
+
+                // Retrieve the new [ new name - old name ] map for the given
+                // descriptor, creating a new one if necessary.
+                Map newNameMap          = retrieveNameMap(descriptorMap, descriptor);
+                Map secondaryNewNameMap = secondaryDescriptorMap == null ? null :
+                    (Map)secondaryDescriptorMap.get(descriptor);
+
+                // Find a unique new name.
+                nameFactory.reset();
+
+                do
+                {
+                    newName = nameFactory.nextName();
+                }
+                while (newNameMap.containsKey(newName) ||
+                       (secondaryNewNameMap != null &&
+                        secondaryNewNameMap.containsKey(newName)));
+
+                // Assign the new name.
+                setNewMemberName(memberInfo, newName);
+
+                // Remember not to use the new name again in this name space.
+                newNameMap.put(newName, name);
+            }
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Gets the nested set of new names, based on the given map
+     * [descriptor - nested set of new names] and a given descriptor.
+     * A new empty set is created if necessary.
+     * @param descriptorMap the map of descriptors to [ new name - member info ] maps.
+     * @param descriptor    the class member descriptor.
+     * @return the nested set of new names.
+     */
+    private Map retrieveNameMap(Map descriptorMap, String descriptor)
+    {
+        // See if we can find the nested map with this descriptor key.
+        Map nameMap = (Map)descriptorMap.get(descriptor);
+
+        // Create a new one if not.
+        if (nameMap == null)
+        {
+            nameMap = new HashMap();
+            descriptorMap.put(descriptor, nameMap);
+        }
+
+        return nameMap;
+    }
+
+
+    /**
+     * Assigns a new name to the given class member.
+     * @param memberInfo the given class member.
+     * @param name       the new name.
+     */
+    static void setNewMemberName(MemberInfo memberInfo, String name)
+    {
+        MemberInfoLinker.lastMemberInfo(memberInfo).setVisitorInfo(name);
+    }
+
+
+    /**
+     * Retrieves the new name of the given class member.
+     * @param memberInfo the given class member.
+     * @return the class member's new name, or <code>null</code> if it doesn't
+     *         have one yet.
+     */
+    static String newMemberName(MemberInfo memberInfo)
+    {
+        return (String)MemberInfoLinker.lastMemberInfo(memberInfo).getVisitorInfo();
+    }
+}
diff --git a/src/proguard/obfuscate/MultiMappingProcessor.java b/src/proguard/obfuscate/MultiMappingProcessor.java
new file mode 100644
index 0000000..14de028
--- /dev/null
+++ b/src/proguard/obfuscate/MultiMappingProcessor.java
@@ -0,0 +1,98 @@
+/* $Id: MultiMappingProcessor.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.util.ClassUtil;
+
+
+/**
+ * This MappingKeeper delegates all method calls to each MappingProcessor
+ * in a given list.
+ *
+ * @author Eric Lafortune
+ */
+public class MultiMappingProcessor implements MappingProcessor
+{
+    private MappingProcessor[] mappingProcessors;
+
+
+    /**
+     * Creates a new MultiMappingProcessor.
+     * @param mappingProcessors the mapping processors to which method calls
+     *                          will be delegated.
+     */
+    public MultiMappingProcessor(MappingProcessor[] mappingProcessors)
+    {
+        this.mappingProcessors = mappingProcessors;
+    }
+
+
+    // Implementations for MappingProcessor.
+
+    public boolean processClassFileMapping(String className,
+                                           String newClassName)
+    {
+        boolean result = false;
+
+        for (int index = 0; index < mappingProcessors.length; index++)
+        {
+            result |= mappingProcessors[index].processClassFileMapping(className,
+                                                                       newClassName);
+        }
+
+        return result;
+    }
+
+
+    public void processFieldMapping(String className,
+                                    String fieldType,
+                                    String fieldName,
+                                    String newFieldName)
+    {
+        for (int index = 0; index < mappingProcessors.length; index++)
+        {
+            mappingProcessors[index].processFieldMapping(className,
+                                                         fieldType,
+                                                         fieldName,
+                                                         newFieldName);
+        }
+    }
+
+
+    public void processMethodMapping(String className,
+                                     int    firstLineNumber,
+                                     int    lastLineNumber,
+                                     String methodReturnType,
+                                     String methodNameAndArguments,
+                                     String newMethodName)
+    {
+        for (int index = 0; index < mappingProcessors.length; index++)
+        {
+            mappingProcessors[index].processMethodMapping(className,
+                                                          firstLineNumber,
+                                                          lastLineNumber,
+                                                          methodReturnType,
+                                                          methodNameAndArguments,
+                                                          newMethodName);
+        }
+    }
+}
diff --git a/src/proguard/obfuscate/NameAndTypeShrinker.java b/src/proguard/obfuscate/NameAndTypeShrinker.java
new file mode 100644
index 0000000..ceccded
--- /dev/null
+++ b/src/proguard/obfuscate/NameAndTypeShrinker.java
@@ -0,0 +1,124 @@
+/* $Id: NameAndTypeShrinker.java,v 1.23 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.editor.ConstantPoolRemapper;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This ClassFileVisitor removes NameAndType constant pool entries
+ * that are not marked as being used.
+ *
+ * @see NameAndTypeUsageMarker
+ *
+ * @author Eric Lafortune
+ */
+public class NameAndTypeShrinker implements ClassFileVisitor
+{
+    private int[]                cpIndexMap;
+    private ConstantPoolRemapper constantPoolRemapper;
+
+
+    /**
+     * Creates a new NameAndTypeShrinker.
+     * @param codeLength an estimate of the maximum length of all the code that
+     *                   will be edited.
+     */
+    public NameAndTypeShrinker(int codeLength)
+    {
+        constantPoolRemapper = new ConstantPoolRemapper(codeLength);
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Shift the used constant pool entries together, filling out the
+        // index map.
+        programClassFile.u2constantPoolCount =
+            shrinkConstantPool(programClassFile.constantPool,
+                               programClassFile.u2constantPoolCount);
+
+
+        // Remap all constant pool references.
+        constantPoolRemapper.setCpIndexMap(cpIndexMap);
+        constantPoolRemapper.visitProgramClassFile(programClassFile);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Removes all NameAndType entries that are not marked as being used
+     * from the given constant pool.
+     * @return the new number of entries.
+     */
+    private int shrinkConstantPool(CpInfo[] constantPool, int length)
+    {
+        // Create a new index map, if necessary.
+        if (cpIndexMap == null ||
+            cpIndexMap.length < length)
+        {
+            cpIndexMap = new int[length];
+        }
+
+        int     counter = 1;
+        boolean isUsed  = false;
+
+        // Shift the used constant pool entries together.
+        for (int index = 1; index < length; index++)
+        {
+            cpIndexMap[index] = counter;
+
+            CpInfo cpInfo = constantPool[index];
+
+            // Don't update the flag if this is the second half of a long entry.
+            if (cpInfo != null)
+            {
+                isUsed = cpInfo.getTag() != ClassConstants.CONSTANT_NameAndType ||
+                         NameAndTypeUsageMarker.isUsed(cpInfo);
+            }
+
+            if (isUsed)
+            {
+                constantPool[counter++] = cpInfo;
+            }
+        }
+
+        // Clear the remaining constant pool elements.
+        for (int index = counter; index < length; index++)
+        {
+            constantPool[index] = null;
+        }
+
+        return counter;
+    }
+}
diff --git a/src/proguard/obfuscate/NameAndTypeUsageMarker.java b/src/proguard/obfuscate/NameAndTypeUsageMarker.java
new file mode 100644
index 0000000..578a9f0
--- /dev/null
+++ b/src/proguard/obfuscate/NameAndTypeUsageMarker.java
@@ -0,0 +1,160 @@
+/* $Id: NameAndTypeUsageMarker.java,v 1.13 2004/11/26 12:46:43 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This ClassFileVisitor marks all NameAndType constant pool entries that are
+ * being used in the classes it visits.
+ *
+ * @see NameAndTypeShrinker
+ *
+ * @author Eric Lafortune
+ */
+public class NameAndTypeUsageMarker
+  implements ClassFileVisitor,
+             CpInfoVisitor,
+             AttrInfoVisitor
+{
+    // A visitor info flag to indicate the NameAndType constant pool entry is being used.
+    private static final Object USED = new Object();
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Mark the NameAndType entries referenced by all other constant pool
+        // entries.
+        programClassFile.constantPoolEntriesAccept(this);
+
+        // Mark the NameAndType entries referenced by all EnclosingMethod
+        // attributes.
+        programClassFile.attributesAccept(this);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+
+
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    {
+        visitRefCpInfo(classFile, fieldrefCpInfo);
+    }
+
+
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    {
+        visitRefCpInfo(classFile, interfaceMethodrefCpInfo);
+    }
+
+
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    {
+        visitRefCpInfo(classFile, methodrefCpInfo);
+    }
+
+
+    private void visitRefCpInfo(ClassFile classFile, RefCpInfo refCpInfo)
+    {
+        markNameAndTypeCpEntry(classFile, refCpInfo.u2nameAndTypeIndex);
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo) {}
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
+
+
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
+    {
+        if (enclosingMethodAttrInfo.u2nameAndTypeIndex != 0)
+        {
+            markNameAndTypeCpEntry(classFile, enclosingMethodAttrInfo.u2nameAndTypeIndex);
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Marks the given UTF-8 constant pool entry of the given class.
+     */
+    private void markNameAndTypeCpEntry(ClassFile classFile, int index)
+    {
+         markAsUsed((NameAndTypeCpInfo)((ProgramClassFile)classFile).getCpEntry(index));
+    }
+
+
+    /**
+     * Marks the given VisitorAccepter as being used.
+     * In this context, the VisitorAccepter will be a NameAndTypeCpInfo object.
+     */
+    private static void markAsUsed(VisitorAccepter visitorAccepter)
+    {
+        visitorAccepter.setVisitorInfo(USED);
+    }
+
+
+    /**
+     * Returns whether the given VisitorAccepter has been marked as being used.
+     * In this context, the VisitorAccepter will be a NameAndTypeCpInfo object.
+     */
+    static boolean isUsed(VisitorAccepter visitorAccepter)
+    {
+        return visitorAccepter.getVisitorInfo() == USED;
+    }
+}
diff --git a/src/proguard/obfuscate/NameFactory.java b/src/proguard/obfuscate/NameFactory.java
new file mode 100644
index 0000000..0d06c5d
--- /dev/null
+++ b/src/proguard/obfuscate/NameFactory.java
@@ -0,0 +1,36 @@
+/* $Id: NameFactory.java,v 1.13 2004/11/01 21:17:51 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import java.util.*;
+
+/**
+ * This interfaces provides methods to generate unique sequences of names.
+ * The names must be valid Java identifiers.
+ *
+ * @author Eric Lafortune
+ */
+public interface NameFactory
+{
+    public void reset();
+
+    public String nextName();
+}
diff --git a/src/proguard/obfuscate/NameMarker.java b/src/proguard/obfuscate/NameMarker.java
new file mode 100644
index 0000000..ea359e9
--- /dev/null
+++ b/src/proguard/obfuscate/NameMarker.java
@@ -0,0 +1,78 @@
+/* $Id: NameMarker.java,v 1.14 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This <code>ClassFileVisitor</code> and <code>MemberInfoVisitor</code>
+ * marks names of the class files and class members it visits. The marked names
+ * will remain unchanged in the obfuscation step.
+ *
+ * @see ClassFileObfuscator
+ * @see MemberInfoObfuscator
+ *
+ * @author Eric Lafortune
+ */
+public class NameMarker
+  implements ClassFileVisitor,
+             MemberInfoVisitor
+{
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Make sure the class name will be kept.
+        ClassFileObfuscator.setNewClassName(programClassFile,
+                                            programClassFile.getName());
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile) {}
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        visitMemberInfo(programClassFile, programFieldInfo);
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        visitMemberInfo(programClassFile, programMethodInfo);
+    }
+
+
+    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    {
+        // Make sure the class member name will be kept.
+        MemberInfoObfuscator.setNewMemberName(programMemberInfo,
+                                              programMemberInfo.getName(programClassFile));
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+}
diff --git a/src/proguard/obfuscate/ReadNameFactory.java b/src/proguard/obfuscate/ReadNameFactory.java
new file mode 100644
index 0000000..97ca12d
--- /dev/null
+++ b/src/proguard/obfuscate/ReadNameFactory.java
@@ -0,0 +1,171 @@
+/* $Id: ReadNameFactory.java,v 1.2 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * This <code>NameFactory</code> generates names that are read from a
+ * specified input file.
+ * Comments (everything starting with '#' on a single line) are ignored.
+ *
+ * @author Eric Lafortune
+ */
+public class ReadNameFactory implements NameFactory
+{
+    private static final char COMMENT_CHARACTER = '#';
+
+
+    private final NameFactory nameFactory;
+    private final List        names = new ArrayList();
+
+    private int index = 0;
+
+
+    /**
+     * Creates a new <code>ReadNameFactory</code>.
+     * @param fileName    the name of the file from which the names can be read.
+     * @param nameFactory the name factory from which names will be retrieved
+     *                    if the list of read names has been exhausted.
+     */
+    public ReadNameFactory(String      fileName,
+                           NameFactory nameFactory) throws IOException
+    {
+        this.nameFactory = nameFactory;
+
+        Reader reader = new FileReader(fileName);
+
+        try
+        {
+            StringBuffer buffer = new StringBuffer();
+
+            while (true)
+            {
+                // Read the next character.
+                int c = reader.read();
+
+                // Is it a valid identifier character?
+                if (c != -1 &&
+                    (buffer.length() == 0 ?
+                         Character.isJavaIdentifierStart((char)c) :
+                         Character.isJavaIdentifierPart((char)c)))
+                {
+                    // Append it to the current identifier.
+                    buffer.append((char)c);
+                }
+                else
+                {
+                    // Did we collect a new identifier?
+                    if (buffer.length() > 0)
+                    {
+                        // Add the completed name to the list of names, if it's
+                        // not in it yet.
+                        String name = buffer.toString();
+                        if (!names.contains(name))
+                        {
+                            names.add(name);
+                        }
+
+                        // Clear the buffer.
+                        buffer.setLength(0);
+                    }
+
+                    // Is this the beginning of a comment line?
+                    if (c == COMMENT_CHARACTER)
+                    {
+                        // Skip all characters till the end of the line.
+                        do
+                        {
+                            c = reader.read();
+                        }
+                        while (c != -1 &&
+                               c != '\n' &&
+                               c != '\r');
+                    }
+
+                    // Is this the end of the file?
+                    if (c == -1)
+                    {
+                        // Just return.
+                        return;
+                    }
+                }
+            }
+        }
+        finally
+        {
+            reader.close();
+        }
+    }
+
+
+    // Implementations for NameFactory.
+
+    public void reset()
+    {
+        index = 0;
+
+        nameFactory.reset();
+    }
+
+
+    public String nextName()
+    {
+        String name;
+
+        // Do we still have names?
+        if (index < names.size())
+        {
+            // Return the next name.
+            name = (String)names.get(index++);
+        }
+        else
+        {
+            // Return the next different name from the other name factory.
+            do
+            {
+                name = nameFactory.nextName();
+            }
+            while (names.contains(name));
+        }
+
+        return name;
+    }
+
+
+    public static void main(String[] args)
+    {
+        try
+        {
+            ReadNameFactory factory = new ReadNameFactory(args[0], new SimpleNameFactory());
+
+            for (int counter = 0; counter < 50; counter++)
+            {
+                System.out.println("["+factory.nextName()+"]");
+            }
+        }
+        catch (IOException ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/src/proguard/obfuscate/SimpleNameFactory.java b/src/proguard/obfuscate/SimpleNameFactory.java
new file mode 100644
index 0000000..aae8e05
--- /dev/null
+++ b/src/proguard/obfuscate/SimpleNameFactory.java
@@ -0,0 +1,156 @@
+/* $Id: SimpleNameFactory.java,v 1.2 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import java.util.*;
+
+
+/**
+ * This <code>NameFactory</code> generates unique short names, using mixed-case
+ * characters or lower-case characters only.
+ *
+ * @author Eric Lafortune
+ */
+public class SimpleNameFactory implements NameFactory
+{
+    private static final int CHARACTER_COUNT = 26;
+
+    private static final List cachedMixedCaseNames = new ArrayList();
+    private static final List cachedLowerCaseNames = new ArrayList();
+
+    private boolean generateMixedCaseNames;
+    private int     index = 0;
+
+
+    /**
+     * Creates a new <code>SimpleNameFactory</code> that generates mixed-case names.
+     */
+    public SimpleNameFactory()
+    {
+        this(true);
+    }
+
+
+    /**
+     * Creates a new <code>SimpleNameFactory</code>.
+     * @param generateMixedCaseNames a flag to indicate whether the generated
+     *                               names will be mixed-case, or lower-case only.
+     */
+    public SimpleNameFactory(boolean generateMixedCaseNames)
+    {
+        this.generateMixedCaseNames = generateMixedCaseNames;
+    }
+
+
+    // Implementations for NameFactory.
+
+    public void reset()
+    {
+        index = 0;
+    }
+
+
+    public String nextName()
+    {
+        return name(index++);
+    }
+
+
+    /**
+     * Returns the name at the given index.
+     */
+    private String name(int index)
+    {
+        // Which cache do we need?
+        List cachedNames = generateMixedCaseNames ?
+            cachedMixedCaseNames :
+            cachedLowerCaseNames;
+
+        // Do we have the name in the cache?
+        if (index < cachedNames.size())
+        {
+            return (String)cachedNames.get(index);
+        }
+
+        // Create a new name and cache it.
+        String name = newName(index);
+        cachedNames.add(index, name);
+
+        return name;
+    }
+
+
+    /**
+     * Creates and returns the name at the given index.
+     */
+    private String newName(int index)
+    {
+        // If we're allowed to generate mixed-case names, we can use twice as
+        // many characters.
+        int totalCharacterCount = generateMixedCaseNames ?
+            2 * CHARACTER_COUNT :
+            CHARACTER_COUNT;
+
+        int baseIndex = index / totalCharacterCount;
+        int offset    = index % totalCharacterCount;
+
+        char newChar = charAt(offset);
+
+        String newName = baseIndex == 0 ?
+            new String(new char[] { newChar }) :
+            (name(baseIndex-1) + newChar);
+
+        return newName;
+    }
+
+
+    /**
+     * Returns the character with the given index, between 0 and the number of
+     * acceptable characters.
+     */
+    private char charAt(int index)
+    {
+        return (char)((index < CHARACTER_COUNT ? 'a' - 0               :
+                                                 'A' - CHARACTER_COUNT) + index);
+    }
+
+
+    public static void main(String[] args)
+    {
+        System.out.println("Some mixed-case names:");
+        printNameSamples(new SimpleNameFactory(true), 60);
+        System.out.println("Some lower-case names:");
+        printNameSamples(new SimpleNameFactory(false), 60);
+        System.out.println("Some more mixed-case names:");
+        printNameSamples(new SimpleNameFactory(true), 80);
+        System.out.println("Some more lower-case names:");
+        printNameSamples(new SimpleNameFactory(false), 80);
+    }
+
+
+    private static void printNameSamples(SimpleNameFactory factory, int count)
+    {
+        for (int counter = 0; counter < count; counter++)
+        {
+            System.out.println("  ["+factory.nextName()+"]");
+        }
+    }
+}
diff --git a/src/proguard/obfuscate/Utf8Shrinker.java b/src/proguard/obfuscate/Utf8Shrinker.java
new file mode 100644
index 0000000..e01bbce
--- /dev/null
+++ b/src/proguard/obfuscate/Utf8Shrinker.java
@@ -0,0 +1,121 @@
+/* $Id: Utf8Shrinker.java,v 1.26 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.editor.ConstantPoolRemapper;
+import proguard.classfile.visitor.ClassFileVisitor;
+
+
+/**
+ * This ClassFileVisitor removes UTF-8 constant pool entries that are not marked
+ * as being used.
+ *
+ * @see Utf8UsageMarker
+ *
+ * @author Eric Lafortune
+ */
+public class Utf8Shrinker implements ClassFileVisitor
+{
+    private int[]                cpIndexMap;
+    private ConstantPoolRemapper constantPoolRemapper;
+
+
+    /**
+     * Creates a new Utf8Shrinker.
+     * @param codeLength an estimate of the maximum length of all the code that
+     *                   will be edited.
+     */
+    public Utf8Shrinker(int codeLength)
+    {
+        constantPoolRemapper = new ConstantPoolRemapper(codeLength);
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Shift the used constant pool entries together, filling out the
+        // index map.
+        programClassFile.u2constantPoolCount =
+            shrinkConstantPool(programClassFile.constantPool,
+                               programClassFile.u2constantPoolCount);
+
+        // Remap all constant pool references.
+        constantPoolRemapper.setCpIndexMap(cpIndexMap);
+        constantPoolRemapper.visitProgramClassFile(programClassFile);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Removes all UTF-8 entries that are not marked as being used
+     * from the given constant pool.
+     * @return the new number of entries.
+     */
+    private int shrinkConstantPool(CpInfo[] constantPool, int length)
+    {
+        // Create a new index map, if necessary.
+        if (cpIndexMap == null ||
+            cpIndexMap.length < length)
+        {
+            cpIndexMap = new int[length];
+        }
+
+        int     counter = 1;
+        boolean isUsed  = false;
+
+        // Shift the used constant pool entries together.
+        for (int index = 1; index < length; index++)
+        {
+            cpIndexMap[index] = counter;
+
+            CpInfo cpInfo = constantPool[index];
+
+            // Don't update the flag if this is the second half of a long entry.
+            if (cpInfo != null)
+            {
+                isUsed = cpInfo.getTag() != ClassConstants.CONSTANT_Utf8 ||
+                         Utf8UsageMarker.isUsed(cpInfo);
+            }
+
+            if (isUsed)
+            {
+                constantPool[counter++] = cpInfo;
+            }
+        }
+
+        // Clear the remaining constant pool elements.
+        for (int index = counter; index < length; index++)
+        {
+            constantPool[index] = null;
+        }
+
+        return counter;
+    }
+}
diff --git a/src/proguard/obfuscate/Utf8UsageMarker.java b/src/proguard/obfuscate/Utf8UsageMarker.java
new file mode 100644
index 0000000..f34abf0
--- /dev/null
+++ b/src/proguard/obfuscate/Utf8UsageMarker.java
@@ -0,0 +1,419 @@
+/* $Id: Utf8UsageMarker.java,v 1.22 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This ClassFileVisitor marks all UTF-8 constant pool entries that are
+ * being used in the classes it visits.
+ *
+ * @see Utf8Shrinker
+ *
+ * @author Eric Lafortune
+ */
+public class Utf8UsageMarker
+  implements ClassFileVisitor,
+             MemberInfoVisitor,
+             CpInfoVisitor,
+             AttrInfoVisitor,
+             InnerClassesInfoVisitor,
+             LocalVariableInfoVisitor,
+             LocalVariableTypeInfoVisitor,
+             AnnotationVisitor,
+             ElementValueVisitor
+{
+    // A visitor info flag to indicate the UTF-8 constant pool entry is being used.
+    private static final Object USED = new Object();
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Mark the UTF-8 entries referenced by the other constant pool entries.
+        programClassFile.constantPoolEntriesAccept(this);
+
+        // Mark the UTF-8 entries referenced by the fields and methods.
+        programClassFile.fieldsAccept(this);
+        programClassFile.methodsAccept(this);
+
+        // Mark the UTF-8 entries referenced by the attributes.
+        programClassFile.attributesAccept(this);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        visitMemberInfo(programClassFile, programFieldInfo);
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        visitMemberInfo(programClassFile, programMethodInfo);
+    }
+
+
+    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    {
+        // Mark the name and descriptor UTF-8 entries.
+        markCpUtf8Entry(programClassFile, programMemberInfo.u2nameIndex);
+        markCpUtf8Entry(programClassFile, programMemberInfo.u2descriptorIndex);
+
+        // Mark the UTF-8 entries referenced by the attributes.
+        programMemberInfo.attributesAccept(programClassFile, this);
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
+
+
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
+    {
+        markCpUtf8Entry(classFile, stringCpInfo.u2stringIndex);
+    }
+
+
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    {
+        markCpUtf8Entry(classFile, classCpInfo.u2nameIndex);
+    }
+
+
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo)
+    {
+        markCpUtf8Entry(classFile, nameAndTypeCpInfo.u2nameIndex);
+        markCpUtf8Entry(classFile, nameAndTypeCpInfo.u2descriptorIndex);
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo)
+    {
+        // This is the best we can do for unknown attributes.
+        markCpUtf8Entry(classFile, unknownAttrInfo.u2attrNameIndex);
+    }
+
+
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
+    {
+        markCpUtf8Entry(classFile, innerClassesAttrInfo.u2attrNameIndex);
+
+        // Mark the UTF-8 entries referenced by the inner classes.
+        innerClassesAttrInfo.innerClassEntriesAccept(classFile, this);
+    }
+
+
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
+    {
+        markCpUtf8Entry(classFile, enclosingMethodAttrInfo.u2attrNameIndex);
+
+        // These entries have already been marked in the constant pool.
+        //classFile.constantPoolEntryAccept(this, enclosingMethodAttrInfo.u2classIndex);
+        //classFile.constantPoolEntryAccept(this, enclosingMethodAttrInfo.u2nameAndTypeIndex);
+    }
+
+
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo)
+    {
+        markCpUtf8Entry(classFile, constantValueAttrInfo.u2attrNameIndex);
+    }
+
+
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo)
+    {
+        markCpUtf8Entry(classFile, exceptionsAttrInfo.u2attrNameIndex);
+    }
+
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        markCpUtf8Entry(classFile, codeAttrInfo.u2attrNameIndex);
+
+        // Mark the UTF-8 entries referenced by the attributes.
+        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
+    }
+
+
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo)
+    {
+        markCpUtf8Entry(classFile, lineNumberTableAttrInfo.u2attrNameIndex);
+    }
+
+
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
+    {
+        markCpUtf8Entry(classFile, localVariableTableAttrInfo.u2attrNameIndex);
+
+        // Mark the UTF-8 entries referenced by the local variables.
+        localVariableTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+    }
+
+
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
+    {
+        markCpUtf8Entry(classFile, localVariableTypeTableAttrInfo.u2attrNameIndex);
+
+        // Mark the UTF-8 entries referenced by the local variable types.
+        localVariableTypeTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+    }
+
+
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo)
+    {
+        markCpUtf8Entry(classFile, sourceFileAttrInfo.u2attrNameIndex);
+
+        markCpUtf8Entry(classFile, sourceFileAttrInfo.u2sourceFileIndex);
+    }
+
+
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo)
+    {
+        markCpUtf8Entry(classFile, sourceDirAttrInfo.u2attrNameIndex);
+
+        markCpUtf8Entry(classFile, sourceDirAttrInfo.u2sourceDirIndex);
+    }
+
+
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo)
+    {
+        markCpUtf8Entry(classFile, deprecatedAttrInfo.u2attrNameIndex);
+    }
+
+
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo)
+    {
+        markCpUtf8Entry(classFile, syntheticAttrInfo.u2attrNameIndex);
+    }
+
+
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
+    {
+        markCpUtf8Entry(classFile, signatureAttrInfo.u2attrNameIndex);
+
+        markCpUtf8Entry(classFile, signatureAttrInfo.u2signatureIndex);
+    }
+
+
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
+    {
+        markCpUtf8Entry(classFile, runtimeVisibleAnnotationsAttrInfo.u2attrNameIndex);
+
+        // Mark the UTF-8 entries referenced by the annotations.
+        runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
+    {
+        markCpUtf8Entry(classFile, runtimeInvisibleAnnotationsAttrInfo.u2attrNameIndex);
+
+        // Mark the UTF-8 entries referenced by the annotations.
+        runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
+    {
+        markCpUtf8Entry(classFile, runtimeVisibleParameterAnnotationsAttrInfo.u2attrNameIndex);
+
+        // Mark the UTF-8 entries referenced by the annotations.
+        runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
+    {
+        markCpUtf8Entry(classFile, runtimeInvisibleParameterAnnotationsAttrInfo.u2attrNameIndex);
+
+        // Mark the UTF-8 entries referenced by the annotations.
+        runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
+    {
+        markCpUtf8Entry(classFile, annotationDefaultAttrInfo.u2attrNameIndex);
+
+        // Mark the UTF-8 entries referenced by the element value.
+        annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
+    }
+
+
+    // Implementations for InnerClassesInfoVisitor.
+
+    public void visitInnerClassesInfo(ClassFile classFile, InnerClassesInfo innerClassesInfo)
+    {
+        if (innerClassesInfo.u2innerNameIndex != 0)
+        {
+            markCpUtf8Entry(classFile, innerClassesInfo.u2innerNameIndex);
+        }
+    }
+
+
+    // Implementations for LocalVariableInfoVisitor.
+
+    public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo)
+    {
+        markCpUtf8Entry(classFile, localVariableInfo.u2nameIndex);
+        markCpUtf8Entry(classFile, localVariableInfo.u2descriptorIndex);
+    }
+
+
+    // Implementations for LocalVariableTypeInfoVisitor.
+
+    public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo)
+    {
+        markCpUtf8Entry(classFile, localVariableTypeInfo.u2nameIndex);
+        markCpUtf8Entry(classFile, localVariableTypeInfo.u2signatureIndex);
+    }
+
+
+    // Implementations for AnnotationVisitor.
+
+    public void visitAnnotation(ClassFile classFile, Annotation annotation)
+    {
+        markCpUtf8Entry(classFile, annotation.u2typeIndex);
+
+        // Mark the UTF-8 entries referenced by the element values.
+        annotation.elementValuesAccept(classFile, this);
+    }
+
+
+    // Implementations for ElementValueVisitor.
+
+    public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue)
+    {
+        if (constantElementValue.u2elementName != 0)
+        {
+            markCpUtf8Entry(classFile, constantElementValue.u2elementName);
+        }
+
+        // Only the string constant element value refers to a UTF-8 entry.
+        if (constantElementValue.u1tag == ClassConstants.ELEMENT_VALUE_STRING_CONSTANT)
+        {
+            markCpUtf8Entry(classFile, constantElementValue.u2constantValueIndex);
+        }
+    }
+
+
+    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+    {
+        if (enumConstantElementValue.u2elementName != 0)
+        {
+            markCpUtf8Entry(classFile, enumConstantElementValue.u2elementName);
+        }
+
+        markCpUtf8Entry(classFile, enumConstantElementValue.u2typeNameIndex);
+        markCpUtf8Entry(classFile, enumConstantElementValue.u2constantNameIndex);
+    }
+
+
+    public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
+    {
+        if (classElementValue.u2elementName != 0)
+        {
+            markCpUtf8Entry(classFile, classElementValue.u2elementName);
+        }
+
+        markCpUtf8Entry(classFile, classElementValue.u2classInfoIndex);
+    }
+
+
+    public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue)
+    {
+        if (annotationElementValue.u2elementName != 0)
+        {
+            markCpUtf8Entry(classFile, annotationElementValue.u2elementName);
+        }
+
+        // Mark the UTF-8 entries referenced by the annotation.
+        annotationElementValue.annotationAccept(classFile, this);
+    }
+
+
+    public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
+    {
+        if (arrayElementValue.u2elementName != 0)
+        {
+            markCpUtf8Entry(classFile, arrayElementValue.u2elementName);
+        }
+
+        // Mark the UTF-8 entries referenced by the element values.
+        arrayElementValue.elementValuesAccept(classFile, annotation, this);
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Marks the given UTF-8 constant pool entry of the given class.
+     */
+    private void markCpUtf8Entry(ClassFile classFile, int index)
+    {
+         markAsUsed((Utf8CpInfo)((ProgramClassFile)classFile).getCpEntry(index));
+    }
+
+
+    /**
+     * Marks the given VisitorAccepter as being used.
+     * In this context, the VisitorAccepter will be a Utf8CpInfo object.
+     */
+    private static void markAsUsed(VisitorAccepter visitorAccepter)
+    {
+        visitorAccepter.setVisitorInfo(USED);
+    }
+
+
+    /**
+     * Returns whether the given VisitorAccepter has been marked as being used.
+     * In this context, the VisitorAccepter will be a Utf8CpInfo object.
+     */
+    static boolean isUsed(VisitorAccepter visitorAccepter)
+    {
+        return visitorAccepter.getVisitorInfo() == USED;
+    }
+}
diff --git a/src/proguard/obfuscate/package.html b/src/proguard/obfuscate/package.html
new file mode 100644
index 0000000..a82d266
--- /dev/null
+++ b/src/proguard/obfuscate/package.html
@@ -0,0 +1,3 @@
+<body>
+This package contains classes to perform obfuscation of class files.
+</body>
diff --git a/src/proguard/optimize/ChangedCodePrinter.java b/src/proguard/optimize/ChangedCodePrinter.java
new file mode 100644
index 0000000..ab6875a
--- /dev/null
+++ b/src/proguard/optimize/ChangedCodePrinter.java
@@ -0,0 +1,203 @@
+/* $Id: ChangedCodePrinter.java,v 1.6 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.util.ClassUtil;
+import proguard.classfile.visitor.*;
+
+/**
+ * This AttrInfoVisitor delegates its call to another AttrInfoVisitor, and
+ * prints out the code if the other visitor has changed it.
+ *
+ * @author Eric Lafortune
+ */
+public class ChangedCodePrinter implements AttrInfoVisitor
+{
+    private AttrInfoVisitor attrInfoVisitor;
+
+
+    public ChangedCodePrinter(AttrInfoVisitor attrInfoVisitor)
+    {
+        this.attrInfoVisitor = attrInfoVisitor;
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo)
+    {
+      attrInfoVisitor.visitUnknownAttrInfo(classFile, unknownAttrInfo);
+    }
+
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
+    {
+      attrInfoVisitor.visitInnerClassesAttrInfo(classFile, innerClassesAttrInfo);
+    }
+
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
+    {
+        attrInfoVisitor.visitEnclosingMethodAttrInfo(classFile, enclosingMethodAttrInfo);
+    }
+
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo)
+    {
+      attrInfoVisitor.visitConstantValueAttrInfo(classFile, fieldInfo, constantValueAttrInfo);
+    }
+
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo)
+    {
+      attrInfoVisitor.visitExceptionsAttrInfo(classFile, methodInfo, exceptionsAttrInfo);
+    }
+
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo)
+    {
+      attrInfoVisitor.visitLineNumberTableAttrInfo(classFile, methodInfo, codeAttrInfo, lineNumberTableAttrInfo);
+    }
+
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
+    {
+      attrInfoVisitor.visitLocalVariableTableAttrInfo(classFile, methodInfo, codeAttrInfo, localVariableTableAttrInfo);
+    }
+
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
+    {
+        attrInfoVisitor.visitLocalVariableTypeTableAttrInfo(classFile, methodInfo, codeAttrInfo, localVariableTypeTableAttrInfo);
+    }
+
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo)
+    {
+      attrInfoVisitor.visitSourceFileAttrInfo(classFile, sourceFileAttrInfo);
+    }
+
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo)
+    {
+      attrInfoVisitor.visitSourceDirAttrInfo(classFile, sourceDirAttrInfo);
+    }
+
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo)
+    {
+      attrInfoVisitor.visitDeprecatedAttrInfo(classFile, deprecatedAttrInfo);
+    }
+
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo)
+    {
+      attrInfoVisitor.visitSyntheticAttrInfo(classFile, syntheticAttrInfo);
+    }
+
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
+    {
+      attrInfoVisitor.visitSignatureAttrInfo(classFile, signatureAttrInfo);
+    }
+
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
+    {
+        attrInfoVisitor.visitRuntimeVisibleAnnotationAttrInfo(classFile, runtimeVisibleAnnotationsAttrInfo);
+    }
+
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
+    {
+        attrInfoVisitor.visitRuntimeInvisibleAnnotationAttrInfo(classFile, runtimeInvisibleAnnotationsAttrInfo);
+    }
+
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
+    {
+        attrInfoVisitor.visitRuntimeVisibleParameterAnnotationAttrInfo(classFile, runtimeVisibleParameterAnnotationsAttrInfo);
+    }
+
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
+    {
+        attrInfoVisitor.visitRuntimeInvisibleParameterAnnotationAttrInfo(classFile, runtimeInvisibleParameterAnnotationsAttrInfo);
+    }
+
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
+    {
+        attrInfoVisitor.visitAnnotationDefaultAttrInfo(classFile, annotationDefaultAttrInfo);
+    }
+
+
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        byte[] code    = codeAttrInfo.code;
+        byte[] oldCode = new byte[code.length];
+
+        // Copy the current code.
+        for (int index = 0; index < codeAttrInfo.u4codeLength; index++)
+        {
+            oldCode[index] = code[index];
+        }
+
+        // Delegate to the real visitor.
+        attrInfoVisitor.visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
+
+        // Check if the code has changed.
+        if (codeHasChanged(codeAttrInfo, oldCode))
+        {
+            printChangedCode(classFile, methodInfo, codeAttrInfo, oldCode);
+        }
+    }
+
+
+    // Small utility methods.
+
+    private boolean codeHasChanged(CodeAttrInfo codeAttrInfo, byte[] oldCode)
+    {
+        if (oldCode.length != codeAttrInfo.u4codeLength)
+        {
+            return true;
+        }
+
+        for (int index = 0; index < codeAttrInfo.u4codeLength; index++)
+        {
+            if (oldCode[index] != codeAttrInfo.code[index])
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+    private void printChangedCode(ClassFile    classFile,
+                                  MethodInfo   methodInfo,
+                                  CodeAttrInfo codeAttrInfo,
+                                  byte[]       oldCode)
+    {
+        System.out.println("Class "+ClassUtil.externalClassName(classFile.getName()));
+        System.out.println("Method "+ClassUtil.externalFullMethodDescription(classFile.getName(),
+                                                                             0,
+                                                                             methodInfo.getName(classFile),
+                                                                             methodInfo.getDescriptor(classFile)));
+
+        for (int index = 0; index < codeAttrInfo.u4codeLength; index++)
+        {
+            System.out.println(
+                (oldCode[index] == codeAttrInfo.code[index]? "  -- ":"  => ")+
+                index+": "+
+                Integer.toHexString(0x100|oldCode[index]          &0xff).substring(1)+" "+
+                Integer.toHexString(0x100|codeAttrInfo.code[index]&0xff).substring(1));
+        }
+    }
+}
diff --git a/src/proguard/optimize/KeepMarker.java b/src/proguard/optimize/KeepMarker.java
new file mode 100644
index 0000000..1f5a52e
--- /dev/null
+++ b/src/proguard/optimize/KeepMarker.java
@@ -0,0 +1,92 @@
+/* $Id: KeepMarker.java,v 1.4 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This <code>ClassFileVisitor</code> and <code>MemberInfoVisitor</code>
+ * marks class files and class members it visits. The marked elements
+ * will remain unchanged as necessary in the optimization step.
+ *
+ * @author Eric Lafortune
+ */
+public class KeepMarker
+  implements ClassFileVisitor,
+             MemberInfoVisitor
+{
+    // A visitor info flag to indicate the visitor accepter is being kept.
+    private static final Object KEPT = new Object();
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Make sure the class will be kept.
+        markAsKept(programClassFile);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile) {}
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        visitMemberInfo(programClassFile, programFieldInfo);
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        visitMemberInfo(programClassFile, programMethodInfo);
+    }
+
+
+    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    {
+        // Make sure the class member will be kept.
+        markAsKept(programMemberInfo);
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+
+
+    // Small utility methods.
+
+    public static void markAsKept(VisitorAccepter visitorAccepter)
+    {
+        visitorAccepter.setVisitorInfo(KEPT);
+    }
+
+
+    public static boolean isKept(VisitorAccepter visitorAccepter)
+    {
+        return visitorAccepter == null                          ||
+               visitorAccepter.getVisitorInfo() == KEPT;
+    }
+}
diff --git a/src/proguard/optimize/NoSideEffectMethodMarker.java b/src/proguard/optimize/NoSideEffectMethodMarker.java
new file mode 100644
index 0000000..2b69044
--- /dev/null
+++ b/src/proguard/optimize/NoSideEffectMethodMarker.java
@@ -0,0 +1,74 @@
+/* $Id: NoSideEffectMethodMarker.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize;
+
+import proguard.classfile.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This MemberInfoVisitor marks all methods that it visits as not having any side
+ * effects. It will make the SideEffectMethodMarker consider them as such
+ * without further analysis.
+ *
+ * @see SideEffectMethodMarker
+ * @author Eric Lafortune
+ */
+public class NoSideEffectMethodMarker
+  implements MemberInfoVisitor
+{
+    // A visitor info flag to indicate that a MethodInfo object doesn't have
+    // anyside effects.
+    private static final Object NO_SIDE_EFFECTS = new Object();
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        markAsNoSideEffects(programMethodInfo);
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        markAsNoSideEffects(libraryMethodInfo);
+    }
+
+
+    // Small utility methods.
+
+    public static void markAsNoSideEffects(VisitorAccepter visitorAccepter)
+    {
+        visitorAccepter.setVisitorInfo(NO_SIDE_EFFECTS);
+    }
+
+
+    public static boolean hasNoSideEffects(VisitorAccepter visitorAccepter)
+    {
+        return visitorAccepter != null &&
+               visitorAccepter.getVisitorInfo() == NO_SIDE_EFFECTS;
+    }
+}
diff --git a/src/proguard/optimize/SideEffectInstructionChecker.java b/src/proguard/optimize/SideEffectInstructionChecker.java
new file mode 100644
index 0000000..ae7d569
--- /dev/null
+++ b/src/proguard/optimize/SideEffectInstructionChecker.java
@@ -0,0 +1,246 @@
+/* $Id: SideEffectInstructionChecker.java,v 1.8 2004/12/11 20:10:10 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This class can tell whether an instruction has any side effects. Return
+ * instructions can be included or not.
+ *
+ * @see WriteOnlyFieldMarker
+ * @see NoSideEffectMethodMarker
+ * @see SideEffectMethodMarker
+ * @author Eric Lafortune
+ */
+public class SideEffectInstructionChecker
+  implements InstructionVisitor,
+             CpInfoVisitor,
+             MemberInfoVisitor
+{
+    private boolean includeReturnInstructions;
+
+    // Parameters and values for visitor methods.
+    private boolean hasSideEffects;
+    private boolean virtual;
+
+
+    public SideEffectInstructionChecker(boolean includeReturnInstructions)
+    {
+        this.includeReturnInstructions = includeReturnInstructions;
+    }
+
+
+    public boolean hasSideEffects(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, Instruction instruction)
+    {
+        hasSideEffects = false;
+
+        instruction.accept(classFile, methodInfo,  codeAttrInfo, offset, this);
+
+        return hasSideEffects;
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
+    {
+        byte opcode = simpleInstruction.opcode;
+
+        // Check for instructions that might cause side effects.
+        if (opcode == InstructionConstants.OP_IASTORE      ||
+            opcode == InstructionConstants.OP_LASTORE      ||
+            opcode == InstructionConstants.OP_FASTORE      ||
+            opcode == InstructionConstants.OP_DASTORE      ||
+            opcode == InstructionConstants.OP_AASTORE      ||
+            opcode == InstructionConstants.OP_BASTORE      ||
+            opcode == InstructionConstants.OP_CASTORE      ||
+            opcode == InstructionConstants.OP_SASTORE      ||
+            opcode == InstructionConstants.OP_ATHROW       ||
+            opcode == InstructionConstants.OP_MONITORENTER ||
+            opcode == InstructionConstants.OP_MONITOREXIT  ||
+            (includeReturnInstructions &&
+             (opcode == InstructionConstants.OP_IRETURN ||
+              opcode == InstructionConstants.OP_LRETURN ||
+              opcode == InstructionConstants.OP_FRETURN ||
+              opcode == InstructionConstants.OP_DRETURN ||
+              opcode == InstructionConstants.OP_ARETURN ||
+              opcode == InstructionConstants.OP_RETURN))) {
+
+            // These instructions always cause a side effect.
+            hasSideEffects = true;
+        }
+
+    }
+
+
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    {
+        byte opcode = variableInstruction.opcode;
+
+        // Check for instructions that might cause side effects.
+        if (includeReturnInstructions &&
+            opcode == InstructionConstants.OP_RET)
+        {
+            hasSideEffects = true;
+        }
+    }
+
+
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    {
+        byte opcode = cpInstruction.opcode;
+
+        // Check for instructions that might cause side effects.
+        if      (opcode == InstructionConstants.OP_PUTSTATIC     ||
+                 opcode == InstructionConstants.OP_PUTFIELD)
+        {
+            // Check if the field is write-only.
+            classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
+        }
+        else if (opcode == InstructionConstants.OP_INVOKESPECIAL ||
+                 opcode == InstructionConstants.OP_INVOKESTATIC)
+        {
+            // Check if the invoked method is causing any side effects.
+            virtual = false;
+            classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
+        }
+        else if (opcode == InstructionConstants.OP_INVOKEVIRTUAL ||
+                 opcode == InstructionConstants.OP_INVOKEINTERFACE)
+        {
+            // Check if the invoked method is causing any side effects.
+            virtual = true;
+            classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
+        }
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+
+
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    {
+        hasSideEffects = !WriteOnlyFieldMarker.isWriteOnly(fieldrefCpInfo.referencedMemberInfo);
+    }
+
+
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    {
+        // Do we have a reference to the interface method?
+        if (interfaceMethodrefCpInfo.referencedMemberInfo == null)
+        {
+            // We'll have to assume the unknown interface method has side effects.
+            hasSideEffects = true;
+        }
+        else
+        {
+            // First check the referenced interface method itself.
+            interfaceMethodrefCpInfo.referencedMemberInfoAccept(this);
+
+            // If the result isn't conclusive, check up and down the hierarchy.
+            if (!hasSideEffects)
+            {
+                String name = interfaceMethodrefCpInfo.getName(classFile);
+                String type = interfaceMethodrefCpInfo.getType(classFile);
+
+                // Check all implementations of the method.
+                // First go to  all concrete classes of the interface.
+                // From there, travel up and down the class hierarchy to check
+                // the method.
+                //
+                // This way, we're also catching retro-fitted interfaces, where
+                // a class's implementation of an interface method is hiding
+                // higher up its class hierarchy.
+                interfaceMethodrefCpInfo.referencedClassAccept(
+                    new ConcreteClassFileDownTraveler(
+                    new ClassFileHierarchyTraveler(true, true, false, true,
+                    new NamedMethodVisitor(name, type, this))));
+            }
+        }
+    }
+
+
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    {
+        // Do we have a reference to the method?
+        if (methodrefCpInfo.referencedMemberInfo == null)
+        {
+            // We'll have to assume the unknown method has side effects.
+            hasSideEffects = true;
+        }
+        else
+        {
+            // First check the referenced method itself.
+            methodrefCpInfo.referencedMemberInfoAccept(this);
+
+            // If the result isn't conclusive, check down the hierarchy.
+            if (!hasSideEffects &&
+                virtual)
+            {
+                String name = methodrefCpInfo.getName(classFile);
+                String type = methodrefCpInfo.getType(classFile);
+
+                // Check all overriding implementations of the method,
+                // down the class hierarchy.
+                methodrefCpInfo.referencedClassAccept(
+                    new ClassFileHierarchyTraveler(false, false, false, true,
+                    new NamedMethodVisitor(name, type, this)));
+            }
+        }
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        hasSideEffects = hasSideEffects ||
+                         SideEffectMethodMarker.hasSideEffects(programMethodInfo);
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        hasSideEffects = hasSideEffects ||
+                         !NoSideEffectMethodMarker.hasNoSideEffects(libraryMethodInfo);
+    }
+}
diff --git a/src/proguard/optimize/SideEffectMethodMarker.java b/src/proguard/optimize/SideEffectMethodMarker.java
new file mode 100644
index 0000000..982500c
--- /dev/null
+++ b/src/proguard/optimize/SideEffectMethodMarker.java
@@ -0,0 +1,172 @@
+/* $Id: SideEffectMethodMarker.java,v 1.3 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This ClassPoolVisitor marks all methods that have side effects.
+ *
+ * @see WriteOnlyFieldMarker
+ * @see NoSideEffectMethodMarker
+ * @author Eric Lafortune
+ */
+public class SideEffectMethodMarker
+  implements ClassPoolVisitor,
+             ClassFileVisitor,
+             MemberInfoVisitor,
+             AttrInfoVisitor
+{
+    // A visitor info flag to indicate that a MethodInfo object has side effects.
+    private static final Object SIDE_EFFECTS = new Object();
+
+
+    // A reusable object for checking whether instructions have side effects.
+    private SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(false);
+
+    // Parameters and values for visitor methods.
+    private int     newSideEffectCount;
+    private boolean hasSideEffects;
+
+
+    // Implementations for ClassPoolVisitor.
+
+    public void visitClassPool(ClassPool classPool)
+    {
+        // Go over all class files and their methods, marking if they have side
+        // effects, until no new cases can be found.
+        do
+        {
+            newSideEffectCount = 0;
+
+            // Go over all class files and their methods once.
+            classPool.classFilesAccept(this);
+        }
+        while (newSideEffectCount > 0);
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Go over all methods.
+        programClassFile.methodsAccept(this);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile) {}
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        if (!hasSideEffects(programMethodInfo) &&
+            !NoSideEffectMethodMarker.hasNoSideEffects(programMethodInfo))
+        {
+            // Set the return value, in case the method doesn't have a code
+            // attribute (a native method or an abstract method).
+            hasSideEffects = (programMethodInfo.getAccessFlags() & ClassConstants.INTERNAL_ACC_NATIVE) != 0;
+
+            programMethodInfo.attributesAccept(programClassFile, this);
+
+            // Mark the method depending on the return value.
+            if (hasSideEffects)
+            {
+                markAsSideEffects(programMethodInfo);
+
+                newSideEffectCount++;
+            }
+        }
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
+
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        byte[] code   = codeAttrInfo.code;
+        int    length = codeAttrInfo.u4codeLength;
+
+        // Go over all instructions.
+        int offset = 0;
+        do
+        {
+            // Get the current instruction.
+            Instruction instruction = InstructionFactory.create(code, offset);
+
+            // Check if it is causing any side effects.
+            hasSideEffects = sideEffectInstructionChecker.hasSideEffects(classFile, methodInfo, codeAttrInfo, offset, instruction);
+
+            // Go to the next instruction.
+            offset += instruction.length(offset);
+        }
+        while (offset < length && !hasSideEffects);
+    }
+
+
+    // Small utility methods.
+
+    public static void markAsSideEffects(VisitorAccepter visitorAccepter)
+    {
+        visitorAccepter.setVisitorInfo(SIDE_EFFECTS);
+    }
+
+
+    public static boolean hasSideEffects(VisitorAccepter visitorAccepter)
+    {
+        return visitorAccepter == null                          ||
+               visitorAccepter.getVisitorInfo() == SIDE_EFFECTS ||
+               KeepMarker.isKept(visitorAccepter);
+    }
+}
diff --git a/src/proguard/optimize/WriteOnlyFieldMarker.java b/src/proguard/optimize/WriteOnlyFieldMarker.java
new file mode 100644
index 0000000..5d7e773
--- /dev/null
+++ b/src/proguard/optimize/WriteOnlyFieldMarker.java
@@ -0,0 +1,158 @@
+/* $Id: WriteOnlyFieldMarker.java,v 1.5 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This InstructionVisitor marks all fields that are write-only.
+ *
+ * @author Eric Lafortune
+ */
+public class WriteOnlyFieldMarker
+  implements InstructionVisitor,
+             CpInfoVisitor,
+             MemberInfoVisitor
+{
+    // Visitor info flags to indicate whether a FieldInfo object is write-only.
+    private static final Object READ       = new Object();
+    private static final Object WRITE_ONLY = new Object();
+
+
+    // Parameters and values for visitor methods.
+    private boolean reading;
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+
+
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    {
+        byte opcode = cpInstruction.opcode;
+
+        // Check for instructions that involve fields.
+        if      (opcode == InstructionConstants.OP_GETSTATIC     ||
+                 opcode == InstructionConstants.OP_GETFIELD)
+        {
+            // Mark the field as being read from.
+            reading = true;
+            classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
+        }
+        else if (opcode == InstructionConstants.OP_PUTSTATIC     ||
+                 opcode == InstructionConstants.OP_PUTFIELD)
+        {
+            // Mark the field as being written to.
+            reading = false;
+            classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
+        }
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+
+
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    {
+        MemberInfo referencedMemberInfo = fieldrefCpInfo.referencedMemberInfo;
+
+        if (referencedMemberInfo != null)
+        {
+            referencedMemberInfo.accept(fieldrefCpInfo.referencedClassFile, this);
+        }
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        // Hasn't the field been marked as being read yet?
+        if (!isRead(programFieldInfo))
+        {
+            // Mark it now, depending on whther this is a reading operation
+            // or a writing operation.
+            if (reading)
+            {
+                markAsRead(programFieldInfo);
+            }
+            else
+            {
+                markAsWriteOnly(programFieldInfo);
+            }
+        }
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo) {}
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+
+
+    // Small utility methods.
+
+    private static void markAsRead(VisitorAccepter visitorAccepter)
+    {
+        visitorAccepter.setVisitorInfo(READ);
+    }
+
+
+    private static boolean isRead(VisitorAccepter visitorAccepter)
+    {
+        return visitorAccepter == null                  ||
+               visitorAccepter.getVisitorInfo() == READ ||
+               KeepMarker.isKept(visitorAccepter);
+    }
+
+
+    public static void markAsWriteOnly(VisitorAccepter visitorAccepter)
+    {
+        visitorAccepter.setVisitorInfo(WRITE_ONLY);
+    }
+
+
+    public static boolean isWriteOnly(VisitorAccepter visitorAccepter)
+    {
+        return visitorAccepter != null                        &&
+               visitorAccepter.getVisitorInfo() == WRITE_ONLY &&
+               !KeepMarker.isKept(visitorAccepter);
+    }
+}
diff --git a/src/proguard/optimize/evaluation/BranchUnit.java b/src/proguard/optimize/evaluation/BranchUnit.java
new file mode 100644
index 0000000..e2f405d
--- /dev/null
+++ b/src/proguard/optimize/evaluation/BranchUnit.java
@@ -0,0 +1,64 @@
+/* $Id: BranchUnit.java,v 1.3 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.optimize.evaluation.value.*;
+
+/**
+ * This InstructionVisitor evaluates the instructions that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public interface BranchUnit
+{
+    /**
+     * Sets the new instruction offset.
+     */
+    public void branch(ClassFile    classFile,
+                       CodeAttrInfo codeAttrInfo,
+                       int          offset,
+                       int          branchTarget);
+
+
+    /**
+     * Sets the new instruction offset, depending on the certainty of the
+     * conditional branch.
+     */
+    public void branchConditionally(ClassFile    classFile,
+                                    CodeAttrInfo codeAttrInfo,
+                                    int          offset,
+                                    int          branchTarget,
+                                    int          conditional);
+
+
+    /**
+     * Returns from the method with the given value.
+     */
+    public void returnFromMethod(Value returnValue);
+
+
+    /**
+     * Handles the throwing of an exception.
+     */
+    public void throwException();
+}
diff --git a/src/proguard/optimize/evaluation/PartialEvaluator.java b/src/proguard/optimize/evaluation/PartialEvaluator.java
new file mode 100644
index 0000000..766ae68
--- /dev/null
+++ b/src/proguard/optimize/evaluation/PartialEvaluator.java
@@ -0,0 +1,2016 @@
+/* $Id: PartialEvaluator.java,v 1.26 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.editor.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+import proguard.optimize.*;
+import proguard.optimize.evaluation.value.*;
+
+/**
+ * This MemberInfoVisitor performs partial evaluation on the program methods
+ * that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class PartialEvaluator
+implements   MemberInfoVisitor,
+             AttrInfoVisitor,
+             ExceptionInfoVisitor,
+             InstructionVisitor,
+             CpInfoVisitor
+{
+    //*
+    private static final boolean DEBUG_RESULTS  = false;
+    private static final boolean DEBUG_ANALYSIS = false;
+    private static final boolean DEBUG          = false;
+    /*/
+    private static boolean DEBUG_RESULTS  = true;
+    private static boolean DEBUG_ANALYSIS = true;
+    private static boolean DEBUG          = true;
+    //*/
+
+    private static final int INITIAL_CODE_LENGTH = 1024;
+    private static final int INITIAL_VALUE_COUNT = 32;
+
+    private static final int MAXIMUM_EVALUATION_COUNT = 100;
+
+
+    private InstructionOffsetValue[] varTraceValues     = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
+    private InstructionOffsetValue[] stackTraceValues   = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
+    private InstructionOffsetValue[] branchOriginValues = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
+    private InstructionOffsetValue[] branchTargetValues = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
+    private TracedVariables[]        vars               = new TracedVariables[INITIAL_CODE_LENGTH];
+    private TracedStack[]            stacks             = new TracedStack[INITIAL_CODE_LENGTH];
+    private int[]                    evaluationCounts   = new int[INITIAL_CODE_LENGTH];
+    private boolean[]                initialization     = new boolean[INITIAL_CODE_LENGTH];
+    private boolean[]                isNecessary        = new boolean[INITIAL_CODE_LENGTH];
+    private boolean                  evaluateExceptions;
+
+    private TracedVariables  parameters = new TracedVariables(INITIAL_VALUE_COUNT);
+    private TracedVariables  variables  = new TracedVariables(INITIAL_VALUE_COUNT);
+    private TracedStack      stack      = new TracedStack(INITIAL_VALUE_COUNT);
+    private TracedBranchUnit branchUnit = new TracedBranchUnit();
+
+    private ClassFileCleaner             classFileCleaner             = new ClassFileCleaner();
+    private SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true);
+    private CodeAttrInfoEditor           codeAttrInfoEditor           = new CodeAttrInfoEditor(INITIAL_CODE_LENGTH);
+
+    private boolean isInitializer;
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+//        DEBUG = DEBUG_ANALYSIS = DEBUG_RESULTS =
+//            programClassFile.getName().equals("abc/Def") &&
+//            programMethodInfo.getName(programClassFile).equals("abc");
+
+        // Initialize the parameters.
+        boolean isStatic =
+            (programMethodInfo.u2accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0;
+
+        // Count the number of parameters, taking into account their Categories.
+        String parameterDescriptor = programMethodInfo.getDescriptor(programClassFile);
+        int count = (isStatic ? 0 : 1) +
+                    ClassUtil.internalMethodParameterSize(parameterDescriptor);
+
+        // Reuse the existing parameters object, ensuring the right size.
+        parameters.reset(count);
+
+        // Go over the parameters again.
+        InternalTypeEnumeration internalTypeEnumeration =
+            new InternalTypeEnumeration(parameterDescriptor);
+
+        int index = 0;
+
+        // Clear the store value of each parameter.
+        parameters.setStoreValue(InstructionOffsetValueFactory.create());
+
+        // Put the caller's reference in parameter 0.
+        if (!isStatic)
+        {
+            parameters.store(index++, ReferenceValueFactory.create(false));
+        }
+
+        while (internalTypeEnumeration.hasMoreTypes())
+        {
+            String type = internalTypeEnumeration.nextType();
+
+            // Get a generic corresponding value.
+            Value value = ValueFactory.create(type);
+
+            // Store the value in the parameter.
+            parameters.store(index, value);
+
+            // Increment the index according to the Category of the value.
+            index += value.isCategory2() ? 2 : 1;
+        }
+
+        // Reset the return value.
+        branchUnit.setTraceReturnValue(null);
+
+        try
+        {
+            // Process the code.
+            programMethodInfo.attributesAccept(programClassFile, this);
+        }
+        catch (RuntimeException ex)
+        {
+            // TODO: Remove this when the partial evaluator has stabilized.
+            System.err.println("Unexpected error while optimizing after partial evaluation:");
+            System.err.println("  ClassFile   = ["+programClassFile.getName()+"]");
+            System.err.println("  Method      = ["+programMethodInfo.getName(programClassFile)+programMethodInfo.getDescriptor(programClassFile)+"]");
+            System.err.println("Not optimizing this method");
+
+            if (DEBUG)
+            {
+                throw ex;
+            }
+        }
+
+        if (DEBUG)
+        {
+            Value returnValue = branchUnit.getTraceReturnValue();
+            if (returnValue != null)
+            {
+                System.out.println("Return value for method "+
+                                   ClassUtil.externalFullMethodDescription(programClassFile.getName(),
+                                                                           0,
+                                                                           programMethodInfo.getName(programClassFile),
+                                                                           programMethodInfo.getDescriptor(programClassFile))+
+                                   " -> ["+returnValue.toString()+"]");
+                System.out.println();
+            }
+        }
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
+
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        if (DEBUG_RESULTS)
+        {
+            System.out.println();
+            System.out.println("Class "+ClassUtil.externalClassName(classFile.getName()));
+            System.out.println("Method "+ClassUtil.externalFullMethodDescription(classFile.getName(),
+                                                                                 0,
+                                                                                 methodInfo.getName(classFile),
+                                                                                 methodInfo.getDescriptor(classFile)));
+            System.out.println("  Params:"+parameters);
+        }
+
+        int codeLength = codeAttrInfo.u4codeLength;
+
+        // Reset the code changes.
+        codeAttrInfoEditor.reset(codeLength);
+
+        if (DEBUG)
+        {
+            System.out.println("  Max locals = "+codeAttrInfo.u2maxLocals);
+            System.out.println("  Max stack  = "+codeAttrInfo.u2maxStack);
+        }
+
+        // Create new arrays for storing a stack and a set of variables at each
+        // branch target.
+        if (isNecessary.length < codeLength)
+        {
+            varTraceValues     = new InstructionOffsetValue[codeLength];
+            stackTraceValues   = new InstructionOffsetValue[codeLength];
+            branchOriginValues = new InstructionOffsetValue[codeLength];
+            branchTargetValues = new InstructionOffsetValue[codeLength];
+            vars               = new TracedVariables[codeLength];
+            stacks             = new TracedStack[codeLength];
+            evaluationCounts   = new int[codeLength];
+            initialization     = new boolean[codeLength];
+            isNecessary        = new boolean[codeLength];
+        }
+        else
+        {
+            for (int index = 0; index < codeLength; index++)
+            {
+                varTraceValues[index]     = null;
+                stackTraceValues[index]   = null;
+                branchOriginValues[index] = null;
+                branchTargetValues[index] = null;
+                evaluationCounts[index]   = 0;
+                initialization[index]     = false;
+                isNecessary[index]        = false;
+
+                if (vars[index] != null)
+                {
+                    vars[index].reset(codeAttrInfo.u2maxLocals);
+                }
+
+                if (stacks[index] != null)
+                {
+                    stacks[index].reset(codeAttrInfo.u2maxStack);
+                }
+            }
+        }
+
+        // Reuse the existing variables and stack objects, ensuring the right size.
+        variables.reset(codeAttrInfo.u2maxLocals);
+        stack.reset(codeAttrInfo.u2maxStack);
+
+        // Initialize the local variables with the parameters.
+        variables.initialize(parameters);
+
+        // Evaluate the instructions, starting at the entry point.
+        if (DEBUG) System.out.println("Partial evaluation: ");
+
+        evaluateInstructionBlock(classFile,
+                                 methodInfo,
+                                 codeAttrInfo,
+                                 variables,
+                                 stack,
+                                 branchUnit,
+                                 0);
+
+        // Evaluate the exception catch blocks, until their entry variables
+        // have stabilized.
+        do
+        {
+            // Reset the flag to stop evaluating.
+            evaluateExceptions = false;
+
+            // Evaluate all relevant exception catch blocks once.
+            codeAttrInfo.exceptionsAccept(classFile, methodInfo, this);
+        }
+        while (evaluateExceptions);
+
+        // Clean up the visitor information in the exceptions right away.
+        codeAttrInfo.exceptionsAccept(classFile, methodInfo, classFileCleaner);
+
+        // Replace any instructions that can be simplified.
+        if (DEBUG_ANALYSIS) System.out.println("Instruction simplification:");
+
+        codeAttrInfo.instructionsAccept(classFile, methodInfo, this);
+
+
+        // Mark all essential instructions that have been encountered as used.
+        if (DEBUG_ANALYSIS) System.out.println("Usage initialization: ");
+
+        // The invocation of the "super" or "this" <init> method inside a
+        // constructor is always necessary, even if it is assumed to have no
+        // side effects.
+        boolean markSuperOrThis = 
+            methodInfo.getName(classFile).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT);
+
+        int aload0Index = 0;
+
+        int index = 0;
+        do
+        {
+            if (isTraced(index))
+            {
+                Instruction instruction = InstructionFactory.create(codeAttrInfo.code,
+                                                                    index);
+
+                // Remember the most recent aload0 instruction index.
+                if (instruction.opcode == InstructionConstants.OP_ALOAD_0)
+                {
+                    aload0Index = index;
+                }
+
+                // Mark the instruction as necessary if it is the first
+                // invocation of the "super" or "this" <init> method
+                // inside a constructor.
+                else if (markSuperOrThis &&
+                         instruction.opcode == InstructionConstants.OP_INVOKESPECIAL &&
+                         stackTraceValues[index].contains(aload0Index))
+                {
+                    markSuperOrThis = false;
+                    
+                    if (DEBUG_ANALYSIS) System.out.print(index+",");
+                    isNecessary[index] = true;
+                }
+                
+                // Mark the instruction as necessary if it has side effects.
+                else if (sideEffectInstructionChecker.hasSideEffects(classFile,
+                                                                     methodInfo,
+                                                                     codeAttrInfo,
+                                                                     index,
+                                                                     instruction))
+                {
+                    if (DEBUG_ANALYSIS) System.out.print(index+",");
+                    isNecessary[index] = true;
+                }
+            }
+
+            index++;
+        }
+        while (index < codeLength);
+        if (DEBUG_ANALYSIS) System.out.println();
+
+
+        // Mark all other instructions on which the essential instructions
+        // depend. Instead of doing this recursively, we loop across all
+        // instructions, starting at the last one, and restarting at any
+        // higher, previously unmarked instruction that is being marked.
+        if (DEBUG_ANALYSIS) System.out.println("Usage marking:");
+
+        int lowestNecessaryIndex = codeLength;
+        index = codeLength - 1;
+        do
+        {
+            int nextIndex = index - 1;
+
+            // Update the lowest index of all marked instructions higher up.
+            if (isNecessary[index])
+            {
+                lowestNecessaryIndex = index;
+            }
+
+            // Check if this instruction is a branch origin from a branch that
+            // straddles some marked code.
+            nextIndex = markStraddlingBranches(index,
+                                               branchTargetValues[index],
+                                               true,
+                                               lowestNecessaryIndex,
+                                               nextIndex);
+
+            // Mark the instructions on which this instruction depends.
+            nextIndex = markDependencies(index,
+                                         nextIndex);
+
+            // Update the lowest index of all marked instructions higher up.
+            if (isNecessary[index])
+            {
+                lowestNecessaryIndex = index;
+            }
+
+            // Check if this instruction is a branch target from a branch that
+            // straddles some marked code.
+            nextIndex = markStraddlingBranches(index,
+                                               branchOriginValues[index],
+                                               false,
+                                               lowestNecessaryIndex,
+                                               nextIndex);
+
+            if (DEBUG_ANALYSIS)
+            {
+                if (nextIndex >= index)
+                {
+                    System.out.println();
+                }
+            }
+
+            // Update the lowest index of all marked instructions higher up.
+            if (isNecessary[index])
+            {
+                lowestNecessaryIndex = index;
+            }
+
+            // Update the index of the instruction to be investigated next.
+            index = nextIndex;
+        }
+        while (index >= 0);
+        if (DEBUG_ANALYSIS) System.out.println();
+
+
+        // Insert pop instructions where necessary, to keep the stack consistent.
+        if (DEBUG_ANALYSIS) System.out.println("Stack consistency marking:");
+
+        index = codeLength - 1;
+        do
+        {
+            if (isTraced(index) &&
+                !isNecessary[index])
+            {
+                // Make sure the stack is always consistent at this offset.
+                fixStackConsistency(classFile,
+                                    codeAttrInfo,
+                                    index);
+            }
+
+            index--;
+        }
+        while (index >= 0);
+        if (DEBUG_ANALYSIS) System.out.println();
+
+
+        // Fix dup/swap instructions where necessary, to keep the stack consistent.
+        if (DEBUG_ANALYSIS) System.out.println("Dup/swap fixing:");
+
+        index = 0;
+        do
+        {
+            if (isNecessary[index])
+            {
+                // Make sure any dup/swap instructions are always consistent at this offset.
+                fixDupInstruction(codeAttrInfo,
+                                  index);
+            }
+
+            index++;
+        }
+        while (index < codeLength);
+        if (DEBUG_ANALYSIS) System.out.println();
+
+
+        // Mark branches straddling just inserted push/pop instructions.
+        if (DEBUG_ANALYSIS) System.out.println("Final straddling branch marking:");
+
+        lowestNecessaryIndex = codeLength;
+        index = codeLength - 1;
+        do
+        {
+            int nextIndex = index - 1;
+
+            // Update the lowest index of all marked instructions higher up.
+            if (isNecessary[index])
+            {
+                lowestNecessaryIndex = index;
+            }
+
+            // Check if this instruction is a branch origin from a branch that
+            // straddles some marked code.
+            nextIndex = markAndSimplifyStraddlingBranches(index,
+                                                          branchTargetValues[index],
+                                                          lowestNecessaryIndex,
+                                                          nextIndex);
+
+            // Update the lowest index of all marked instructions higher up.
+            if (isNecessary[index])
+            {
+                lowestNecessaryIndex = index;
+            }
+
+            // Check if this instruction is a branch target from a branch that
+            // straddles some marked code.
+            nextIndex = markAndSimplifyStraddlingBranches(branchOriginValues[index],
+                                                          index,
+                                                          lowestNecessaryIndex,
+                                                          nextIndex);
+
+            if (DEBUG_ANALYSIS)
+            {
+                if (nextIndex >= index)
+                {
+                    System.out.println();
+                }
+            }
+
+            // Update the lowest index of all marked instructions higher up.
+            if (isNecessary[index])
+            {
+                lowestNecessaryIndex = index;
+            }
+
+            // Update the index of the instruction to be investigated next.
+            index = nextIndex;
+        }
+        while (index >= 0);
+        if (DEBUG_ANALYSIS) System.out.println();
+
+
+        // Mark variable initializations, even if they aren't strictly necessary.
+        // The virtual machine is not smart enough to see this, and may complain
+        // otherwise.
+        if (DEBUG_ANALYSIS) System.out.println("Initialization marking: ");
+
+        // TODO: Find a better way.
+        index = 0;
+        do
+        {
+            // Is it an initialization that hasn't been marked yet?
+            if (initialization[index] &&
+                !isNecessary[index])
+            {
+                if (DEBUG_ANALYSIS) System.out.println(index+",");
+
+                // Figure out what kind of initialization value has to be stored.
+                int pushInstructionOffset = stackTraceValues[index].instructionOffset(0);
+                int pushComputationalType = stacks[pushInstructionOffset].getTop(0).computationalType();
+                increaseStackSize(index, pushComputationalType, false);
+            }
+
+            index++;
+        }
+        while (index < codeLength);
+        if (DEBUG_ANALYSIS) System.out.println();
+
+
+        if (DEBUG_RESULTS)
+        {
+            System.out.println("Results:");
+            int offset = 0;
+            do
+            {
+                Instruction instruction = InstructionFactory.create(codeAttrInfo.code,
+                                                                    offset);
+                System.out.println((isNecessary[offset] ? " + " : " - ")+instruction.toString(offset));
+                if (isTraced(offset))
+                {
+                    if (varTraceValues[offset] != null &&
+                        varTraceValues[offset].instructionOffsetCount() > 0)
+                    {
+                        System.out.println("     has overall been using information from instructions setting vars: "+varTraceValues[offset]);
+                    }
+                    if (stackTraceValues[offset] != null &&
+                        stackTraceValues[offset].instructionOffsetCount() > 0)
+                    {
+                        System.out.println("     has overall been using information from instructions setting stack: "+stackTraceValues[offset]);
+                    }
+                    if (branchTargetValues[offset] != null)
+                    {
+                        System.out.println("     has overall been branching to "+branchTargetValues[offset]);
+                    }
+                    if (codeAttrInfoEditor.preInsertions[offset] != null)
+                    {
+                        System.out.println("     is preceded by: "+codeAttrInfoEditor.preInsertions[offset]);
+                    }
+                    if (codeAttrInfoEditor.postInsertions[offset] != null)
+                    {
+                        System.out.println("     is followed by: "+codeAttrInfoEditor.preInsertions[offset]);
+                    }
+                    System.out.println("     Vars:  "+vars[offset]);
+                    System.out.println("     Stack: "+stacks[offset]);
+                }
+
+                offset += instruction.length(offset);
+            }
+            while (offset < codeLength);
+        }
+
+        // Delete all instructions that are not used.
+        int offset = 0;
+        do
+        {
+            Instruction instruction = InstructionFactory.create(codeAttrInfo.code,
+                                                                offset);
+            if (!isNecessary[offset])
+            {
+                codeAttrInfoEditor.replaceInstruction(offset, null);
+                codeAttrInfoEditor.replaceInstruction2(offset, null);
+            }
+
+            offset += instruction.length(offset);
+        }
+        while (offset < codeLength);
+
+        // Apply all accumulated changes to the code.
+        codeAttrInfoEditor.visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
+    }
+
+
+    /**
+     * Marks the instructions at the given offsets, if the current instruction
+     * itself has been marked.
+     * @param index     the offset of the current instruction.
+     * @param nextIndex the index of the instruction to be investigated next.
+     * @return the updated index of the instruction to be investigated next.
+     *         It is always greater than or equal, because instructions are
+     *         investigated starting at the highest index.
+     */
+    private int markDependencies(int index,
+                                 int nextIndex)
+    {
+        if (isNecessary[index] &&
+            !codeAttrInfoEditor.isModified(index))
+        {
+            if (DEBUG_ANALYSIS) System.out.print(index);
+
+            // Mark all instructions whose variable values are used.
+            nextIndex = markDependencies(varTraceValues[index], nextIndex);
+
+            // Mark all instructions whose stack values are used.
+            nextIndex = markDependencies(stackTraceValues[index], nextIndex);
+
+            if (DEBUG_ANALYSIS) System.out.print(",");
+        }
+
+        return nextIndex;
+    }
+
+
+    /**
+     * Marks the instructions at the given offsets.
+     * @param traceOffsetValue the offsets of the instructions to be marked.
+     * @param nextIndex        the index of the instruction to be investigated
+     *                         next.
+     * @return the updated index of the instruction to be investigated next.
+     *         It is always greater than or equal, because instructions are
+     *         investigated starting at the highest index.
+     */
+    private int markDependencies(InstructionOffsetValue traceOffsetValue,
+                                 int                    nextIndex)
+    {
+        if (traceOffsetValue != null)
+        {
+            int traceOffsetCount = traceOffsetValue.instructionOffsetCount();
+            for (int traceOffsetIndex = 0; traceOffsetIndex < traceOffsetCount; traceOffsetIndex++)
+            {
+                // Has the other instruction been marked yet?
+                int traceOffset = traceOffsetValue.instructionOffset(traceOffsetIndex);
+                if (!isNecessary[traceOffset])
+                {
+                    if (DEBUG_ANALYSIS) System.out.print("["+traceOffset+"]");
+
+                    // Mark it.
+                    isNecessary[traceOffset] = true;
+
+                    // Restart at this instruction if it has a higher offset.
+                    if (nextIndex < traceOffset)
+                    {
+                        if (DEBUG_ANALYSIS) System.out.print("!");
+
+                        nextIndex = traceOffset;
+                    }
+                }
+            }
+        }
+
+        return nextIndex;
+    }
+
+
+    /**
+     * Marks the branch instructions of straddling branches, if they straddle
+     * some code that has been marked.
+     * @param index                the offset of the branch origin or branch target.
+     * @param branchValue          the offsets of the straddling branch targets
+     *                             or branch origins.
+     * @param isPointingToTargets  <code>true</code> if the above offsets are
+     *                             branch targets, <code>false</code> if they
+     *                             are branch origins.
+     * @param lowestNecessaryIndex the lowest offset of all instructions marked
+     *                             so far.
+     * @param nextIndex            the index of the instruction to be investigated
+     *                             next.
+     * @return the updated index of the instruction to be investigated next.
+     *         It is always greater than or equal the original index, because
+     *         instructions are investigated starting at the highest index.
+     */
+    private int markStraddlingBranches(int                    index,
+                                       InstructionOffsetValue branchValue,
+                                       boolean                isPointingToTargets,
+                                       int                    lowestNecessaryIndex,
+                                       int                    nextIndex)
+    {
+        if (branchValue != null)
+        {
+            // Loop over all branch origins.
+            int branchCount = branchValue.instructionOffsetCount();
+            for (int branchIndex = 0; branchIndex < branchCount; branchIndex++)
+            {
+                // Is the branch straddling any necessary instructions?
+                int branch = branchValue.instructionOffset(branchIndex);
+
+                // Is the offset pointing to a branch origin or to a branch target?
+                nextIndex = isPointingToTargets ?
+                    markStraddlingBranch(index, branch, lowestNecessaryIndex, nextIndex) :
+                    markStraddlingBranch(branch, index, lowestNecessaryIndex, nextIndex);
+            }
+        }
+
+        return nextIndex;
+    }
+
+
+    /**
+     * Marks the given branch instruction, if it straddles some code that has
+     * been marked.
+     * @param branchOrigin         the branch origin.
+     * @param branchTarget         the branch target.
+     * @param lowestNecessaryIndex the lowest offset of all instructions marked
+     *                             so far.
+     * @param nextIndex            the index of the instruction to be investigated
+     *                             next.
+     * @return the updated index of the instruction to be investigated next.
+     *         It is always greater than or equal the original index, because
+     *         instructions are investigated starting at the highest index.
+     */
+    private int markStraddlingBranch(int branchOrigin,
+                                     int branchTarget,
+                                     int lowestNecessaryIndex,
+                                     int nextIndex)
+    {
+        // Has the branch origin been marked yet, and is it straddling the
+        // lowest necessary instruction?
+        if (!isNecessary[branchOrigin] &&
+            isStraddlingBranch(branchOrigin, branchTarget, lowestNecessaryIndex))
+        {
+            if (DEBUG_ANALYSIS) System.out.print("["+branchOrigin+"->"+branchTarget+"]");
+
+            // Mark the branch origin.
+            isNecessary[branchOrigin] = true;
+
+            // Restart at the branch origin if it has a higher offset.
+            if (nextIndex < branchOrigin)
+            {
+                if (DEBUG_ANALYSIS) System.out.print("!");
+
+                nextIndex = branchOrigin;
+            }
+        }
+
+        return nextIndex;
+    }
+
+
+    /**
+     * Marks and simplifies the branch instructions of straddling branches,
+     * if they straddle some code that has been marked.
+     * @param branchOrigin         the branch origin.
+     * @param branchTargets        the branch targets.
+     * @param lowestNecessaryIndex the lowest offset of all instructions marked
+     *                             so far.
+     * @param nextIndex            the index of the instruction to be investigated
+     *                             next.
+     * @return the updated index of the instruction to be investigated next.
+     *         It is always greater than or equal the original index, because
+     *         instructions are investigated starting at the highest index.
+     */
+    private int markAndSimplifyStraddlingBranches(int                    branchOrigin,
+                                                  InstructionOffsetValue branchTargets,
+                                                  int                    lowestNecessaryIndex,
+                                                  int                    nextIndex)
+    {
+        if (branchTargets != null &&
+            !isNecessary[branchOrigin])
+        {
+            // Loop over all branch targets.
+            int branchCount = branchTargets.instructionOffsetCount();
+            if (branchCount > 0)
+            {
+                for (int branchIndex = 0; branchIndex < branchCount; branchIndex++)
+                {
+                    // Is the branch straddling any necessary instructions?
+                    int branchTarget = branchTargets.instructionOffset(branchIndex);
+
+                    if (!isStraddlingBranch(branchOrigin,
+                                            branchTarget,
+                                            lowestNecessaryIndex))
+                    {
+                        return nextIndex;
+                    }
+                }
+
+                nextIndex = markAndSimplifyStraddlingBranch(branchOrigin,
+                                                            branchTargets.instructionOffset(0),
+                                                            lowestNecessaryIndex,
+                                                            nextIndex);
+            }
+        }
+
+        return nextIndex;
+    }
+
+
+    /**
+     * Marks and simplifies the branch instructions of straddling branches,
+     * if they straddle some code that has been marked.
+     * @param branchOrigins        the branch origins.
+     * @param branchTarget         the branch target.
+     * @param lowestNecessaryIndex the lowest offset of all instructions marked
+     *                             so far.
+     * @param nextIndex            the index of the instruction to be investigated
+     *                             next.
+     * @return the updated index of the instruction to be investigated next.
+     *         It is always greater than or equal the original index, because
+     *         instructions are investigated starting at the highest index.
+     */
+    private int markAndSimplifyStraddlingBranches(InstructionOffsetValue branchOrigins,
+                                                  int                    branchTarget,
+                                                  int                    lowestNecessaryIndex,
+                                                  int                    nextIndex)
+    {
+        if (branchOrigins != null)
+        {
+            // Loop over all branch origins.
+            int branchCount = branchOrigins.instructionOffsetCount();
+            for (int branchIndex = 0; branchIndex < branchCount; branchIndex++)
+            {
+                // Is the branch straddling any necessary instructions?
+                int branchOrigin = branchOrigins.instructionOffset(branchIndex);
+
+                nextIndex = markAndSimplifyStraddlingBranch(branchOrigin,
+                                                            branchTarget,
+                                                            lowestNecessaryIndex,
+                                                            nextIndex);
+            }
+        }
+
+        return nextIndex;
+    }
+
+
+    /**
+     * Marks and simplifies the given branch instruction, if it straddles some
+     * code that has been marked.
+     * @param branchOrigin         the branch origin.
+     * @param branchTarget         the branch target.
+     * @param lowestNecessaryIndex the lowest offset of all instructions marked
+     *                             so far.
+     * @param nextIndex            the index of the instruction to be investigated
+     *                             next.
+     * @return the updated index of the instruction to be investigated next.
+     *         It is always greater than or equal the original index, because
+     *         instructions are investigated starting at the highest index.
+     */
+    private int markAndSimplifyStraddlingBranch(int branchOrigin,
+                                                int branchTarget,
+                                                int lowestNecessaryIndex,
+                                                int nextIndex)
+    {
+        // Has the branch origin been marked yet, and is it straddling the
+        // lowest necessary instruction?
+        if (!isNecessary[branchOrigin] &&
+            isStraddlingBranch(branchOrigin, branchTarget, lowestNecessaryIndex))
+        {
+            if (DEBUG_ANALYSIS) System.out.print("["+branchOrigin+"->"+branchTarget+"]");
+
+            // Mark the branch origin.
+            isNecessary[branchOrigin] = true;
+
+            // Replace the branch instruction by a simple branch instrucion.
+            Instruction replacementInstruction =
+                new BranchInstruction(InstructionConstants.OP_GOTO_W,
+                                      branchTarget - branchOrigin).shrink();
+
+            codeAttrInfoEditor.replaceInstruction(branchOrigin,
+                                                  replacementInstruction);
+
+            // Restart at the branch origin if it has a higher offset.
+            if (nextIndex < branchOrigin)
+            {
+                if (DEBUG_ANALYSIS) System.out.print("!");
+
+                nextIndex = branchOrigin;
+            }
+        }
+
+        return nextIndex;
+    }
+
+
+    /**
+     * Returns whether the given branch straddling some code that has been marked.
+     * @param branchOrigin         the branch origin.
+     * @param branchTarget         the branch target.
+     * @param lowestNecessaryIndex the lowest offset of all instructions marked
+     *                             so far.
+     */
+    private boolean isStraddlingBranch(int branchOrigin,
+                                       int branchTarget,
+                                       int lowestNecessaryIndex)
+    {
+        return branchOrigin <= lowestNecessaryIndex ^
+               branchTarget <= lowestNecessaryIndex;
+    }
+
+
+    /**
+     * Inserts pop instructions where necessary, in order to make sure the
+     * stack is consistent at the given index.
+     * @param classFile    the class file that is being checked.
+     * @param codeAttrInfo the code that is being checked.
+     * @param index        the offset of the dependent instruction.
+     */
+    private void fixStackConsistency(ClassFile    classFile,
+                                     CodeAttrInfo codeAttrInfo,
+                                     int          index)
+    {
+        // Is the unnecessary instruction popping values (but not a dup/swap
+        // instruction)?
+        Instruction popInstruction = InstructionFactory.create(codeAttrInfo.code,
+                                                               index);
+        byte popOpcode = popInstruction.opcode;
+
+        int popCount = popInstruction.stackPopCount(classFile);
+        if (popCount > 0) // &&
+            //popOpcode != InstructionConstants.OP_DUP     &&
+            //popOpcode != InstructionConstants.OP_DUP_X1  &&
+            //popOpcode != InstructionConstants.OP_DUP_X2  &&
+            //popOpcode != InstructionConstants.OP_DUP2    &&
+            //popOpcode != InstructionConstants.OP_DUP2_X1 &&
+            //popOpcode != InstructionConstants.OP_DUP2_X2 &&
+            //popOpcode != InstructionConstants.OP_SWAP)
+        {
+            // Check the instructions on which it depends.
+            InstructionOffsetValue traceOffsetValue = stackTraceValues[index];
+            int traceOffsetCount = traceOffsetValue.instructionOffsetCount();
+
+            if (popCount <= 4 &&
+                isAllNecessary(traceOffsetValue))
+            {
+                if (popOpcode == InstructionConstants.OP_POP ||
+                    popOpcode == InstructionConstants.OP_POP2)
+                {
+                    if (DEBUG_ANALYSIS) System.out.println("  Popping value again at "+popInstruction.toString(index)+" (pushed at all "+traceOffsetValue.instructionOffsetCount()+" offsets)");
+
+                    // Simply mark pop and pop2 instructions.
+                    isNecessary[index] = true;
+                }
+                else
+                {
+                    if (DEBUG_ANALYSIS) System.out.println("  Popping value instead of "+popInstruction.toString(index)+" (pushed at all "+traceOffsetValue.instructionOffsetCount()+" offsets)");
+
+                    // Make sure the pushed value is popped again,
+                    // right before this instruction.
+                    decreaseStackSize(index, popCount, true, true);
+                }
+            }
+            //else if (popCount == (popInstruction.isCategory2() ? 4 : 2) &&
+            //         traceOffsetCount == 2                              &&
+            //         isAnyNecessary(traceOffsetValue))
+            //{
+            //    if (DEBUG_ANALYSIS) System.out.println("  Popping single value instead of "+popInstruction.toString(index)+" (pushed at some of "+traceOffsetValue.instructionOffsetCount()+" offsets)");
+            //
+            //    // Make sure the single pushed value is popped again,
+            //    // right before this instruction.
+            //    decreaseStackSize(index, popCount / 2, true, true);
+            //}
+            else if (isAnyNecessary(traceOffsetValue))
+            {
+                if (DEBUG_ANALYSIS) System.out.println("  Popping value somewhere before "+index+" (pushed at some of "+traceOffsetValue.instructionOffsetCount()+" offsets):");
+
+                // Go over all stack pushing instructions.
+                for (int traceOffsetIndex = 0; traceOffsetIndex < traceOffsetCount; traceOffsetIndex++)
+                {
+                    // Has the push instruction been marked?
+                    int pushInstructionOffset = traceOffsetValue.instructionOffset(traceOffsetIndex);
+                    if (isNecessary[pushInstructionOffset])
+                    {
+                        Instruction pushInstruction = InstructionFactory.create(codeAttrInfo.code,
+                                                                                pushInstructionOffset);
+
+                        int lastOffset = lastPopInstructionOffset(pushInstructionOffset,
+                                                                  index,
+                                                                  pushInstructionOffset);
+
+                        if (DEBUG_ANALYSIS) System.out.println("    Popping value right after "+lastOffset+", due to push at "+pushInstructionOffset);
+
+                        // Make sure the pushed value is popped again,
+                        // right after the instruction that pushes it
+                        // (or after the dup instruction that still uses it).
+                        decreaseStackSize(lastOffset,
+                                          pushInstruction.stackPushCount(classFile),
+                                          false, false);
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Returns the last offset of the necessary instruction that depends on the
+     * stack result of the instruction at the given index.
+     * @param startOffset           the start offset in the search.
+     * @param endOffset             the end offset in the search.
+     * @param pushInstructionOffset the offset of the instruction that pushes
+     *                              a result onto the stack.
+     * @return the last offset of the necessary instruction that uses the
+     *         above result.
+     */
+    private int lastPopInstructionOffset(int startOffset,
+                                         int endOffset,
+                                         int pushInstructionOffset)
+    {
+        int lastOffset = startOffset;
+
+        for (int index = startOffset; index < endOffset; index++)
+        {
+            if (isNecessary[index] &&
+                stackTraceValues[index].contains(pushInstructionOffset))
+            {
+                lastOffset = index;
+            }
+        }
+
+        return lastOffset;
+    }
+
+
+    /**
+     * Puts the required push instruction before the given index. The
+     * instruction is marked as necessary.
+     * @param index             the offset of the instruction.
+     * @param computationalType the computational type on the stack, for
+     *                          push instructions.
+     * @param delete            specifies whether the instruction should be
+     *                          deleted.
+     */
+    private void increaseStackSize(int     index,
+                                   int     computationalType,
+                                   boolean delete)
+    {
+        // Mark this instruction.
+        isNecessary[index] = true;
+
+        // Create a simple push instrucion.
+        byte replacementOpcode =
+            computationalType == Value.TYPE_INTEGER   ? InstructionConstants.OP_ICONST_0    :
+            computationalType == Value.TYPE_LONG      ? InstructionConstants.OP_LCONST_0    :
+            computationalType == Value.TYPE_FLOAT     ? InstructionConstants.OP_FCONST_0    :
+            computationalType == Value.TYPE_DOUBLE    ? InstructionConstants.OP_DCONST_0    :
+            computationalType == Value.TYPE_REFERENCE ? InstructionConstants.OP_ACONST_NULL :
+                                                        InstructionConstants.OP_NOP;
+
+        Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
+
+        // Insert the pop or push instruction.
+        codeAttrInfoEditor.insertBeforeInstruction(index,
+                                                   replacementInstruction);
+
+        // Delete the original instruction if necessary.
+        if (delete)
+        {
+            codeAttrInfoEditor.deleteInstruction(index);
+        }
+    }
+
+
+    /**
+     * Puts the required pop instruction at the given index. The
+     * instruction is marked as necessary.
+     * @param offset   the offset of the instruction.
+     * @param popCount the required reduction of the stack size.
+     * @param before   specifies whether the pop instruction should be inserted
+     *                 before or after the present instruction.
+     * @param delete   specifies whether the instruction should be deleted.
+     */
+    private void decreaseStackSize(int     offset,
+                                   int     popCount,
+                                   boolean before,
+                                   boolean delete)
+    {
+        boolean after = !before;
+
+        // Special case: we may replace the instruction by two pop instructions.
+        if (delete && popCount > 2)
+        {
+            before = true;
+            after  = true;
+        }
+
+        if (popCount < 1 ||
+            popCount > 4)
+        {
+            throw new IllegalArgumentException("Unsupported stack size reduction ["+popCount+"]");
+        }
+
+        // Mark this instruction.
+        isNecessary[offset] = true;
+
+        if (before)
+        {
+            // Create a simple pop instrucion.
+            byte replacementOpcode = popCount == 1 || popCount == 3 ?
+                InstructionConstants.OP_POP :
+                InstructionConstants.OP_POP2;
+
+            Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
+
+            // Insert the pop instruction.
+            codeAttrInfoEditor.insertBeforeInstruction(offset,
+                                                       replacementInstruction);
+        }
+
+        if (after)
+        {
+            // Create a simple pop instrucion.
+            byte replacementOpcode = popCount == 1 ?
+                InstructionConstants.OP_POP :
+                InstructionConstants.OP_POP2;
+
+            Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
+
+            if (DEBUG_ANALYSIS) System.out.println("    Pop instruction after ["+offset+"]: "+replacementInstruction);
+ 
+            // Insert the pop instruction.
+            codeAttrInfoEditor.insertAfterInstruction(offset,
+                                                      replacementInstruction);
+        }
+
+        // Delete the original instruction if necessary.
+        if (delete)
+        {
+            codeAttrInfoEditor.deleteInstruction(offset);
+        }
+    }
+
+
+    /**
+     * Replaces the specified instruction by the proper dup/swap variant,
+     * if necessary, depending on the state of the stack.
+     * @param codeAttrInfo the code that is being checked.
+     * @param offset       the offset of the instruction.
+     */
+    private void fixDupInstruction(CodeAttrInfo codeAttrInfo,
+                                   int          offset)
+    {
+        byte replacementOpcode = 0;
+
+        // Simplify the popping instruction if possible.
+        switch (codeAttrInfo.code[offset])
+        {
+            case InstructionConstants.OP_DUP_X1:
+                if (!isStackEntryPresent(offset, 1))
+                {
+                    replacementOpcode = InstructionConstants.OP_DUP;
+                }
+                break;
+
+            case InstructionConstants.OP_DUP_X2:
+                if (!isStackEntryPresent(offset, 1) ||
+                    !isStackEntryPresent(offset, 2))
+                {
+                    if (isStackEntryPresent(offset, 1) ||
+                        isStackEntryPresent(offset, 2))
+                    {
+                        replacementOpcode = InstructionConstants.OP_DUP_X1;
+                    }
+                    else
+                    {
+                        replacementOpcode = InstructionConstants.OP_DUP;
+                    }
+                }
+                break;
+
+            case InstructionConstants.OP_DUP2_X1:
+                if (!isStackEntryPresent(offset, 2))
+                {
+                    replacementOpcode = InstructionConstants.OP_DUP2;
+                }
+                break;
+
+            case InstructionConstants.OP_DUP2_X2:
+                if (!isStackEntryPresent(offset, 2) ||
+                    !isStackEntryPresent(offset, 3))
+                {
+                    if (isStackEntryPresent(offset, 2) ||
+                        isStackEntryPresent(offset, 3))
+                    {
+                        replacementOpcode = InstructionConstants.OP_DUP2_X1;
+                    }
+                    else
+                    {
+                        replacementOpcode = InstructionConstants.OP_DUP2;
+                    }
+                }
+                break;
+
+            case InstructionConstants.OP_SWAP:
+                if (!isStackEntryPresent(offset, 0))
+                {
+                    isNecessary[offset] = false;
+                }
+                break;
+        }
+
+        // Actually replace the instruction with the new opcde, if any.
+        if (replacementOpcode != 0)
+        {
+            Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
+            codeAttrInfoEditor.replaceInstruction(offset,
+                                                  replacementInstruction);
+
+            if (DEBUG_ANALYSIS) System.out.println("  Replacing instruction at ["+offset+"] by "+replacementInstruction.toString());
+        }
+    }
+
+
+    /**
+     * Returns whether the given stack entry is present after execution of the
+     * instruction at the given offset.
+     */
+    private boolean isStackEntryPresent(int instructionOffset, int stackIndex)
+    {
+        return isAnyNecessary(stacks[instructionOffset].getTopTraceValue(stackIndex).instructionOffsetValue());
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo)
+    {
+        if (isTraced(exceptionInfo.u2startpc, exceptionInfo.u2endpc))
+        {
+            if (DEBUG) System.out.println("Partial evaluation of exception ["+exceptionInfo.u2startpc+","+exceptionInfo.u2endpc+"] -> ["+exceptionInfo.u2handlerpc+"]:");
+
+            // Generalize the variables of the try block.
+            variables.reset(codeAttrInfo.u2maxLocals);
+            generalizeVariables(exceptionInfo.u2startpc,
+                                exceptionInfo.u2endpc,
+                                variables);
+
+            // Remember the entry variables of the exception.
+            TracedVariables exceptionVariables = (TracedVariables)exceptionInfo.getVisitorInfo();
+            if (exceptionVariables == null)
+            {
+                exceptionVariables = new TracedVariables(codeAttrInfo.u2maxLocals);
+
+                exceptionInfo.setVisitorInfo(exceptionVariables);
+            }
+            else
+            {
+                // Bail out if the entry variables are the same as last time.
+                if (exceptionVariables.equals(variables))
+                {
+                    if (DEBUG) System.out.println("  Repeated initial variables");
+
+                    return;
+                }
+            }
+
+            exceptionVariables.initialize(variables);
+
+            // Reuse the existing variables and stack objects, ensuring the right size.
+            variables.reset(codeAttrInfo.u2maxLocals);
+            stack.reset(codeAttrInfo.u2maxStack);
+
+            // The initial stack doesn't have associated instruction offsets.
+            Value storeValue = InstructionOffsetValueFactory.create();
+            variables.setStoreValue(storeValue);
+            stack.setStoreValue(storeValue);
+
+            // Initialize the local variables and the stack.
+            variables.initialize(exceptionVariables);
+            //stack.push(ReferenceValueFactory.create((ClassCpInfo)((ProgramClassFile)classFile).getCpEntry(exceptionInfo.u2catchType), false));
+            stack.push(ReferenceValueFactory.create(false));
+
+            // Evaluate the instructions, starting at the entry point.
+            evaluateInstructionBlock(classFile,
+                                     methodInfo,
+                                     codeAttrInfo,
+                                     variables,
+                                     stack,
+                                     branchUnit,
+                                     exceptionInfo.u2handlerpc);
+
+            // Remember to check this exception and other exceptions once more.
+            evaluateExceptions = true;
+        }
+    }
+
+
+    /**
+     * Returns whether a block of instructions is ever used.
+     */
+    private boolean isTraced(int startOffset, int endOffset)
+    {
+        for (int index = startOffset; index < endOffset; index++)
+        {
+            if (isTraced(index))
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Generalize the local variable frames of a block of instructions.
+     */
+    private void generalizeVariables(int startOffset, int endOffset, TracedVariables generalizedVariables)
+    {
+        for (int index = startOffset; index < endOffset; index++)
+        {
+            if (isTraced(index))
+            {
+                // We can't use the return value, because local generalization
+                // can be different a couple of times, with the global
+                // generalization being the same.
+                generalizedVariables.generalize(vars[index]);
+            }
+        }
+    }
+
+
+    // Utility methods to evaluate instruction blocks.
+
+    /**
+     * Evaluates a block of instructions, starting at the given offset and ending
+     * at a branch instruction, a return instruction, or a throw instruction.
+     */
+    private void evaluateInstructionBlock(ClassFile        classFile,
+                                          MethodInfo       methodInfo,
+                                          CodeAttrInfo     codeAttrInfo,
+                                          TracedVariables  variables,
+                                          TracedStack      stack,
+                                          TracedBranchUnit branchUnit,
+                                          int              instructionOffset)
+    {
+        byte[] code = codeAttrInfo.code;
+
+        if (DEBUG)
+        {
+             System.out.println("Instruction block starting at ["+instructionOffset+"] in "+
+                                ClassUtil.externalFullMethodDescription(classFile.getName(),
+                                                                        0,
+                                                                        methodInfo.getName(classFile),
+                                                                        methodInfo.getDescriptor(classFile)));
+             System.out.println("Init vars:  "+variables);
+             System.out.println("Init stack: "+stack);
+        }
+
+        Processor processor = new Processor(variables, stack, branchUnit);
+
+        // Evaluate the subsequent instructions.
+        while (true)
+        {
+            // Maintain a generalized trace instruction offset.
+            int evaluationCount = evaluationCounts[instructionOffset]++;
+            if (evaluationCount == 0)
+            {
+                varTraceValues[instructionOffset]   = InstructionOffsetValueFactory.create();
+                stackTraceValues[instructionOffset] = InstructionOffsetValueFactory.create();
+            }
+
+            // Remember this instruction's offset with any stored value.
+            Value storeValue = InstructionOffsetValueFactory.create(instructionOffset);
+            variables.setStoreValue(storeValue);
+            stack.setStoreValue(storeValue);
+
+            // Reset the trace value.
+            InstructionOffsetValue traceValue = InstructionOffsetValueFactory.create();
+            variables.setTraceValue(traceValue);
+            stack.setTraceValue(traceValue);
+
+            // Reset the initialization flag.
+            variables.resetInitialization();
+
+            // Note that the instruction is only volatile.
+            Instruction instruction = InstructionFactory.create(code, instructionOffset);
+
+            // By default, the next instruction will be the one after this
+            // instruction.
+            int nextInstructionOffset = instructionOffset +
+                                        instruction.length(instructionOffset);
+            InstructionOffsetValue nextInstructionOffsetValue = InstructionOffsetValueFactory.create(nextInstructionOffset);
+            branchUnit.resetCalled();
+            branchUnit.setTraceBranchTargets(nextInstructionOffsetValue);
+
+
+            if (DEBUG)
+            {
+                System.out.println(instruction.toString(instructionOffset));
+            }
+
+            try
+            {
+                // Process the instruction. The processor may call
+                // the Variables methods of 'variables',
+                // the Stack methods of 'stack', and
+                // the BranchUnit methods of this evaluator.
+                instruction.accept(classFile, methodInfo, codeAttrInfo, instructionOffset, processor);
+            }
+            catch (RuntimeException ex)
+            {
+                System.err.println("Unexpected error while performing partial evaluation:");
+                System.err.println("  ClassFile   = ["+classFile.getName()+"]");
+                System.err.println("  Method      = ["+methodInfo.getName(classFile)+methodInfo.getDescriptor(classFile)+"]");
+                System.err.println("  Instruction = "+instruction.toString(instructionOffset));
+
+                throw ex;
+            }
+
+            // Collect the offsets of the instructions whose results were used.
+            InstructionOffsetValue variablesTraceValue = variables.getTraceValue().instructionOffsetValue();
+            InstructionOffsetValue stackTraceValue     = stack.getTraceValue().instructionOffsetValue();
+            varTraceValues[instructionOffset] =
+                varTraceValues[instructionOffset].generalize(variablesTraceValue).instructionOffsetValue();
+            stackTraceValues[instructionOffset] =
+                stackTraceValues[instructionOffset].generalize(stackTraceValue).instructionOffsetValue();
+            initialization[instructionOffset] =
+                initialization[instructionOffset] || variables.wasInitialization();
+
+            // Collect the branch targets from the branch unit.
+            InstructionOffsetValue branchTargets = branchUnit.getTraceBranchTargets();
+            int branchTargetCount = branchTargets.instructionOffsetCount();
+
+            // Stop tracing.
+            variables.setTraceValue(traceValue);
+            stack.setTraceValue(traceValue);
+            branchUnit.setTraceBranchTargets(traceValue);
+
+            if (DEBUG)
+            {
+                if (variablesTraceValue.instructionOffsetCount() > 0)
+                {
+                    System.out.println("     has used information from instructions setting vars: "+variablesTraceValue);
+                }
+                if (stackTraceValue.instructionOffsetCount() > 0)
+                {
+                    System.out.println("     has used information from instructions setting stack: "+stackTraceValue);
+                }
+                if (branchUnit.wasCalled())
+                {
+                    System.out.println("     is branching to "+branchTargets);
+                }
+
+                if (varTraceValues[instructionOffset].instructionOffsetCount() > 0)
+                {
+                    System.out.println("     has up til now been using information from instructions setting vars: "+varTraceValues[instructionOffset]);
+                }
+                if (stackTraceValues[instructionOffset].instructionOffsetCount() > 0)
+                {
+                    System.out.println("     has up till now been using information from instructions setting stack: "+stackTraceValues[instructionOffset]);
+                }
+                if (branchTargetValues[instructionOffset] != null)
+                {
+                    System.out.println("     has up till now been branching to "+branchTargetValues[instructionOffset]);
+                }
+
+                System.out.println("     Vars:  "+variables);
+                System.out.println("     Stack: "+stack);
+            }
+
+            // Maintain a generalized local variable frame and stack at this
+            // branch instruction offset.
+            if (vars[instructionOffset] == null)
+            {
+                // First time we're passing by this instruction.
+                // There's not even a context at this index yet.
+                vars[instructionOffset]   = new TracedVariables(variables);
+                stacks[instructionOffset] = new TracedStack(stack);
+            }
+            else if (evaluationCount == 0)
+            {
+                // First time we're passing by this instruction.
+                // Reuse the context objects at this index.
+                vars[instructionOffset].initialize(variables);
+                stacks[instructionOffset].copy(stack);
+            }
+            else
+            {
+                // If there are multiple alternative branches, or if this
+                // instruction has been evaluated an excessive number of
+                // times, then generalize the current context.
+                // TODO: See if we can avoid generalizing the current context.
+                //if (branchTargetCount > 1 ||
+                //    evaluationCount > MAXIMUM_EVALUATION_COUNT)
+                //{
+                //    variables.generalize(vars[instructionOffset]);
+                //    stack.generalize(stacks[instructionOffset]);
+                //}
+
+                boolean vars_changed  = vars[instructionOffset].generalize(variables);
+                boolean stack_changed = stacks[instructionOffset].generalize(stack);
+
+                // Bail out if the current context is the same as last time.
+                if (!vars_changed  &&
+                    !stack_changed &&
+                    branchTargets.equals(branchTargetValues[instructionOffset]))
+                {
+                    if (DEBUG) System.out.println("Repeated variables, stack, and branch targets");
+
+                    break;
+                }
+
+                // Generalize the current context. Note that the most recent
+                // variable values have to remain last in the generalizations,
+                // for the sake of the ret instruction.
+                variables.initialize(vars[instructionOffset]);
+                stack.copy(stacks[instructionOffset]);
+            }
+
+            // Did the branch unit get called?
+            if (branchUnit.wasCalled())
+            {
+                // Accumulate the branch targets at this offset.
+                branchTargetValues[instructionOffset] = branchTargetValues[instructionOffset] == null ?
+                    branchTargets :
+                    branchTargetValues[instructionOffset].generalize(branchTargets).instructionOffsetValue();
+
+                // Are there no branch targets at all?
+                if (branchTargetCount == 0)
+                {
+                    // Exit from this code block.
+                    break;
+                }
+
+                // Accumulate the branch origins at the branch target offsets.
+                InstructionOffsetValue instructionOffsetValue = InstructionOffsetValueFactory.create(instructionOffset);
+                for (int index = 0; index < branchTargetCount; index++)
+                {
+                    int branchTarget = branchTargets.instructionOffset(index);
+                    branchOriginValues[branchTarget] = branchOriginValues[branchTarget] == null ?
+                        instructionOffsetValue:
+                        branchOriginValues[branchTarget].generalize(instructionOffsetValue).instructionOffsetValue();
+                }
+
+                // Are there multiple branch targets?
+                if (branchTargetCount > 1)
+                {
+                    // Handle them recursively and exit from this code block.
+                    for (int index = 0; index < branchTargetCount; index++)
+                    {
+                        if (DEBUG) System.out.println("Alternative branch #"+index+" out of "+branchTargetCount+", from ["+instructionOffset+"] to ["+branchTargets.instructionOffset(index)+"]");
+
+                        evaluateInstructionBlock(classFile,
+                                                 methodInfo,
+                                                 codeAttrInfo,
+                                                 new TracedVariables(variables),
+                                                 new TracedStack(stack),
+                                                 branchUnit,
+                                                 branchTargets.instructionOffset(index));
+                    }
+
+                    break;
+                }
+
+                if (DEBUG) System.out.println("Definite branch from ["+instructionOffset+"] to ["+branchTargets.instructionOffset(0)+"]");
+            }
+
+            // Just continue with the next instruction.
+            instructionOffset = branchTargets.instructionOffset(0);
+        }
+
+        if (DEBUG) System.out.println("Ending processing of instruction block");
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
+    {
+        if (isTraced(offset))
+        {
+            switch (simpleInstruction.opcode)
+            {
+                case InstructionConstants.OP_IALOAD:
+                case InstructionConstants.OP_BALOAD:
+                case InstructionConstants.OP_CALOAD:
+                case InstructionConstants.OP_SALOAD:
+                case InstructionConstants.OP_IADD:
+                case InstructionConstants.OP_ISUB:
+                case InstructionConstants.OP_IMUL:
+                case InstructionConstants.OP_IDIV:
+                case InstructionConstants.OP_IREM:
+                case InstructionConstants.OP_INEG:
+                case InstructionConstants.OP_ISHL:
+                case InstructionConstants.OP_ISHR:
+                case InstructionConstants.OP_IUSHR:
+                case InstructionConstants.OP_IAND:
+                case InstructionConstants.OP_IOR:
+                case InstructionConstants.OP_IXOR:
+                case InstructionConstants.OP_L2I:
+                case InstructionConstants.OP_F2I:
+                case InstructionConstants.OP_D2I:
+                case InstructionConstants.OP_I2B:
+                case InstructionConstants.OP_I2C:
+                case InstructionConstants.OP_I2S:
+                    replaceIntegerPushInstruction(offset, simpleInstruction);
+                    break;
+
+                case InstructionConstants.OP_LALOAD:
+                case InstructionConstants.OP_LADD:
+                case InstructionConstants.OP_LSUB:
+                case InstructionConstants.OP_LMUL:
+                case InstructionConstants.OP_LDIV:
+                case InstructionConstants.OP_LREM:
+                case InstructionConstants.OP_LNEG:
+                case InstructionConstants.OP_LSHL:
+                case InstructionConstants.OP_LSHR:
+                case InstructionConstants.OP_LUSHR:
+                case InstructionConstants.OP_LAND:
+                case InstructionConstants.OP_LOR:
+                case InstructionConstants.OP_LXOR:
+                case InstructionConstants.OP_I2L:
+                case InstructionConstants.OP_F2L:
+                case InstructionConstants.OP_D2L:
+                    replaceLongPushInstruction(offset, simpleInstruction);
+                    break;
+
+                case InstructionConstants.OP_FALOAD:
+                case InstructionConstants.OP_FADD:
+                case InstructionConstants.OP_FSUB:
+                case InstructionConstants.OP_FMUL:
+                case InstructionConstants.OP_FDIV:
+                case InstructionConstants.OP_FREM:
+                case InstructionConstants.OP_FNEG:
+                case InstructionConstants.OP_I2F:
+                case InstructionConstants.OP_L2F:
+                case InstructionConstants.OP_D2F:
+                    replaceFloatPushInstruction(offset, simpleInstruction);
+                    break;
+
+                case InstructionConstants.OP_DALOAD:
+                case InstructionConstants.OP_DADD:
+                case InstructionConstants.OP_DSUB:
+                case InstructionConstants.OP_DMUL:
+                case InstructionConstants.OP_DDIV:
+                case InstructionConstants.OP_DREM:
+                case InstructionConstants.OP_DNEG:
+                case InstructionConstants.OP_I2D:
+                case InstructionConstants.OP_L2D:
+                case InstructionConstants.OP_F2D:
+                    replaceDoublePushInstruction(offset, simpleInstruction);
+                    break;
+
+                case InstructionConstants.OP_AALOAD:
+                    replaceReferencePushInstruction(offset, simpleInstruction);
+                    break;
+            }
+        }
+    }
+
+
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    {
+        if (isTraced(offset))
+        {
+            switch (variableInstruction.opcode)
+            {
+                case InstructionConstants.OP_ILOAD:
+                case InstructionConstants.OP_ILOAD_0:
+                case InstructionConstants.OP_ILOAD_1:
+                case InstructionConstants.OP_ILOAD_2:
+                case InstructionConstants.OP_ILOAD_3:
+                    replaceIntegerPushInstruction(offset, variableInstruction);
+                    break;
+
+                case InstructionConstants.OP_LLOAD:
+                case InstructionConstants.OP_LLOAD_0:
+                case InstructionConstants.OP_LLOAD_1:
+                case InstructionConstants.OP_LLOAD_2:
+                case InstructionConstants.OP_LLOAD_3:
+                    replaceLongPushInstruction(offset, variableInstruction);
+                    break;
+
+                case InstructionConstants.OP_FLOAD:
+                case InstructionConstants.OP_FLOAD_0:
+                case InstructionConstants.OP_FLOAD_1:
+                case InstructionConstants.OP_FLOAD_2:
+                case InstructionConstants.OP_FLOAD_3:
+                    replaceFloatPushInstruction(offset, variableInstruction);
+                    break;
+
+                case InstructionConstants.OP_DLOAD:
+                case InstructionConstants.OP_DLOAD_0:
+                case InstructionConstants.OP_DLOAD_1:
+                case InstructionConstants.OP_DLOAD_2:
+                case InstructionConstants.OP_DLOAD_3:
+                    replaceDoublePushInstruction(offset, variableInstruction);
+                    break;
+
+                case InstructionConstants.OP_ALOAD:
+                case InstructionConstants.OP_ALOAD_0:
+                case InstructionConstants.OP_ALOAD_1:
+                case InstructionConstants.OP_ALOAD_2:
+                case InstructionConstants.OP_ALOAD_3:
+                    replaceReferencePushInstruction(offset, variableInstruction);
+                    break;
+
+            }
+        }
+    }
+
+
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    {
+        // Make sure 'new' instructions (or subsequent 'dup' instructions)
+        // depend on the subsequent initializer calls, in case these calls
+        // are marked as not having any side effects.
+        if (isTraced(offset) &&
+            cpInstruction.opcode == InstructionConstants.OP_INVOKESPECIAL)
+        {
+            // Check if the invoked method is an initalizer.
+            classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
+
+            if (isInitializer)
+            {
+                // Find the previous instruction (assuming there was no branch).
+                int previousOffset = offset - 1;
+                while (!isTraced(previousOffset))
+                {
+                    previousOffset--;
+                }
+
+                // Compute the stack index of the uninitialized object.
+                int stackIndex = stacks[offset].size();
+
+                // Get the (first and presumably only) offset of the instruction
+                // that put it there. This is typically a dup instruction.
+                int newOffset = stacks[previousOffset].getBottomTraceValue(stackIndex).instructionOffsetValue().instructionOffset(0);
+
+                // Add a reverse dependency. The source instruction depends on
+                // the initializer instruction, thus making sure that the latter
+                // is preserved whenever the former is used.
+                stackTraceValues[newOffset] = stackTraceValues[newOffset].generalize(InstructionOffsetValueFactory.create(offset)).instructionOffsetValue();
+            }
+        }
+    }
+
+
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+    {
+        replaceBranchInstruction(offset, branchInstruction);
+    }
+
+
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
+    {
+        replaceBranchInstruction(offset, tableSwitchInstruction);
+    }
+
+
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
+    {
+        replaceBranchInstruction(offset, lookUpSwitchInstruction);
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Replaces the given integer instruction by a simpler push instruction,
+     * if possible.
+     */
+    private void replaceIntegerPushInstruction(int offset, Instruction instruction)
+    {
+        Stack stack = stacks[offset];
+        Value pushedValue = stack.getTop(0);
+        if (pushedValue.isSpecific())
+        {
+            int value = pushedValue.integerValue().value();
+            if (value << 16 >> 16 == value)
+            {
+                replacePushInstruction(offset,
+                                       InstructionConstants.OP_SIPUSH,
+                                       value);
+            }
+        }
+    }
+
+
+    /**
+     * Replaces the given long instruction by a simpler push instruction,
+     * if possible.
+     */
+    private void replaceLongPushInstruction(int offset, Instruction instruction)
+    {
+        Stack stack = stacks[offset];
+        Value pushedValue = stack.getTop(0);
+        if (pushedValue.isSpecific())
+        {
+            long value = pushedValue.longValue().value();
+            if (value == 0L ||
+                value == 1L)
+            {
+                replacePushInstruction(offset,
+                                       (byte)(InstructionConstants.OP_LCONST_0 + value),
+                                       0);
+            }
+        }
+    }
+
+
+    /**
+     * Replaces the given float instruction by a simpler push instruction,
+     * if possible.
+     */
+    private void replaceFloatPushInstruction(int offset, Instruction instruction)
+    {
+        Stack stack = stacks[offset];
+        Value pushedValue = stack.getTop(0);
+        if (pushedValue.isSpecific())
+        {
+            float value = pushedValue.floatValue().value();
+            if (value == 0f ||
+                value == 1f ||
+                value == 2f)
+            {
+                replacePushInstruction(offset,
+                                       (byte)(InstructionConstants.OP_FCONST_0 + value),
+                                       0);
+            }
+        }
+    }
+
+
+    /**
+     * Replaces the given double instruction by a simpler push instruction,
+     * if possible.
+     */
+    private void replaceDoublePushInstruction(int offset, Instruction instruction)
+    {
+        Stack stack = stacks[offset];
+        Value pushedValue = stack.getTop(0);
+        if (pushedValue.isSpecific())
+        {
+            double value = pushedValue.doubleValue().value();
+            if (value == 0.0 ||
+                value == 1.0)
+            {
+                replacePushInstruction(offset,
+                                       (byte)(InstructionConstants.OP_DCONST_0 + value),
+                                       0);
+            }
+        }
+    }
+
+
+    /**
+     * Replaces the given reference instruction by a simpler push instruction,
+     * if possible.
+     */
+    private void replaceReferencePushInstruction(int offset, Instruction instruction)
+    {
+        Stack stack = stacks[offset];
+        Value pushedValue = stack.getTop(0);
+        if (pushedValue.isSpecific())
+        {
+            ReferenceValue value = pushedValue.referenceValue();
+            if (value.isNull() == Value.ALWAYS)
+            {
+                replacePushInstruction(offset,
+                                       InstructionConstants.OP_ACONST_NULL,
+                                       0);
+            }
+        }
+    }
+
+
+    /**
+     * Replaces the instruction at a given offset by a given push instruction.
+     */
+    private void replacePushInstruction(int offset, byte opcode, int value)
+    {
+        // Remember the replacement instruction.
+        Instruction replacementInstruction =
+             new SimpleInstruction(opcode, value).shrink();
+
+        if (DEBUG_ANALYSIS) System.out.println("  Replacing instruction at ["+offset+"] by "+replacementInstruction.toString());
+
+        codeAttrInfoEditor.replaceInstruction(offset, replacementInstruction);
+    }
+
+
+    /**
+     * Deletes the given branch instruction, or replaces it by a simpler branch
+     * instruction, if possible.
+     */
+    private void replaceBranchInstruction(int offset, Instruction instruction)
+    {
+        if (isTraced(offset))
+        {
+            InstructionOffsetValue branchTargetValue = branchTargetValues[offset];
+
+            // Is there exactly one branch target (not from a goto or jsr)?
+            if (branchTargetValue != null &&
+                branchTargetValue.instructionOffsetCount() == 1      &&
+                instruction.opcode != InstructionConstants.OP_GOTO   &&
+                instruction.opcode != InstructionConstants.OP_GOTO_W &&
+                instruction.opcode != InstructionConstants.OP_JSR    &&
+                instruction.opcode != InstructionConstants.OP_JSR_W)
+            {
+                // Is it branching to the next instruction?
+                int branchOffset = branchTargetValue.instructionOffset(0) - offset;
+                if (branchOffset == instruction.length(offset))
+                {
+                    if (DEBUG_ANALYSIS) System.out.println("  Deleting zero branch instruction at ["+offset+"]");
+
+                    // Delete the branch instruction.
+                    codeAttrInfoEditor.deleteInstruction(offset);
+                }
+                else
+                {
+                    // Replace the branch instruction by a simple branch instrucion.
+                    Instruction replacementInstruction =
+                        new BranchInstruction(InstructionConstants.OP_GOTO_W,
+                                              branchOffset).shrink();
+
+                    if (DEBUG_ANALYSIS) System.out.println("  Replacing branch instruction at ["+offset+"] by "+replacementInstruction.toString());
+
+                    codeAttrInfoEditor.replaceInstruction2(offset,
+                                                           replacementInstruction);
+                }
+            }
+        }
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+
+
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    {
+        isInitializer = methodrefCpInfo.getName(classFile).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT);
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns whether any of the instructions at the given offsets are marked as
+     * necessary.
+     */
+    private boolean isAnyNecessary(InstructionOffsetValue traceValue)
+    {
+        int traceCount = traceValue.instructionOffsetCount();
+        if (traceCount == 0)
+        {
+            return true;
+        }
+
+        for (int traceIndex = 0; traceIndex < traceCount; traceIndex++)
+        {
+            if (isNecessary[traceValue.instructionOffset(traceIndex)])
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Returns whether all of the instructions at the given offsets are marked as
+     * necessary.
+     */
+    private boolean isAllNecessary(InstructionOffsetValue traceValue)
+    {
+        int traceCount = traceValue.instructionOffsetCount();
+        for (int traceIndex = 0; traceIndex < traceCount; traceIndex++)
+        {
+            if (!isNecessary[traceValue.instructionOffset(traceIndex)])
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Returns whether the instruction at the given offset has ever been
+     * executed during the partial evaluation.
+     */
+    private boolean isTraced(int instructionOffset)
+    {
+        return evaluationCounts[instructionOffset] > 0;
+    }
+}
diff --git a/src/proguard/optimize/evaluation/Processor.java b/src/proguard/optimize/evaluation/Processor.java
new file mode 100644
index 0000000..409bea4
--- /dev/null
+++ b/src/proguard/optimize/evaluation/Processor.java
@@ -0,0 +1,997 @@
+/* $Id: Processor.java,v 1.12 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.util.ClassUtil;
+import proguard.classfile.visitor.*;
+import proguard.optimize.evaluation.value.*;
+
+/**
+ * This InstructionVisitor executes the instructions that it visits on a given
+ * local variable frame and stack.
+ *
+ * @author Eric Lafortune
+ */
+public class Processor
+implements   InstructionVisitor,
+             CpInfoVisitor
+{
+    private Variables  variables;
+    private Stack      stack;
+    private BranchUnit branchUnit;
+
+    // Fields acting as return parameters for the CpInfoVisitor methods.
+    private int       parameterCount;
+    private Value     cpValue;
+    private ClassFile referencedClassFile;
+    private int       referencedTypeDimensionCount;
+
+
+    /**
+     * Creates a new processor that operates on the given environment.
+     * @param variables  the local variable frame.
+     * @param stack      the local stack.
+     * @param branchUnit the class that can affect the program counter.
+     */
+    public Processor(Variables  variables,
+                     Stack      stack,
+                     BranchUnit branchUnit)
+    {
+        this.variables  = variables;
+        this.stack      = stack;
+        this.branchUnit = branchUnit;
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
+    {
+        switch (simpleInstruction.opcode)
+        {
+            case InstructionConstants.OP_NOP:
+                break;
+
+            case InstructionConstants.OP_ACONST_NULL:
+                stack.push(ReferenceValueFactory.createNull());
+                break;
+
+            case InstructionConstants.OP_ICONST_M1:
+            case InstructionConstants.OP_ICONST_0:
+            case InstructionConstants.OP_ICONST_1:
+            case InstructionConstants.OP_ICONST_2:
+            case InstructionConstants.OP_ICONST_3:
+            case InstructionConstants.OP_ICONST_4:
+            case InstructionConstants.OP_ICONST_5:
+            case InstructionConstants.OP_BIPUSH:
+            case InstructionConstants.OP_SIPUSH:
+                stack.push(IntegerValueFactory.create(simpleInstruction.constant));
+                break;
+
+            case InstructionConstants.OP_LCONST_0:
+            case InstructionConstants.OP_LCONST_1:
+                stack.push(LongValueFactory.create(simpleInstruction.constant));
+                break;
+
+            case InstructionConstants.OP_FCONST_0:
+            case InstructionConstants.OP_FCONST_1:
+            case InstructionConstants.OP_FCONST_2:
+                stack.push(FloatValueFactory.create((float)simpleInstruction.constant));
+                break;
+
+            case InstructionConstants.OP_DCONST_0:
+            case InstructionConstants.OP_DCONST_1:
+                stack.push(DoubleValueFactory.create((double)simpleInstruction.constant));
+                break;
+
+            case InstructionConstants.OP_IALOAD:
+            case InstructionConstants.OP_BALOAD:
+            case InstructionConstants.OP_CALOAD:
+            case InstructionConstants.OP_SALOAD:
+                stack.ipop();
+                stack.apop();
+                stack.push(IntegerValueFactory.create());
+                break;
+
+            case InstructionConstants.OP_LALOAD:
+                stack.ipop();
+                stack.apop();
+                stack.push(LongValueFactory.create());
+                break;
+
+            case InstructionConstants.OP_FALOAD:
+                stack.ipop();
+                stack.apop();
+                stack.push(FloatValueFactory.create());
+                break;
+
+            case InstructionConstants.OP_DALOAD:
+                stack.ipop();
+                stack.apop();
+                stack.push(DoubleValueFactory.create());
+                break;
+
+            case InstructionConstants.OP_AALOAD:
+                stack.ipop();
+                stack.apop();
+                stack.push(ReferenceValueFactory.create(true));
+                break;
+
+            case InstructionConstants.OP_IASTORE:
+            case InstructionConstants.OP_BASTORE:
+            case InstructionConstants.OP_CASTORE:
+            case InstructionConstants.OP_SASTORE:
+                stack.ipop();
+                stack.ipop();
+                stack.apop();
+                break;
+
+            case InstructionConstants.OP_LASTORE:
+                stack.lpop();
+                stack.ipop();
+                stack.apop();
+                break;
+
+            case InstructionConstants.OP_FASTORE:
+                stack.fpop();
+                stack.ipop();
+                stack.apop();
+                break;
+
+            case InstructionConstants.OP_DASTORE:
+                stack.dpop();
+                stack.ipop();
+                stack.apop();
+                break;
+
+            case InstructionConstants.OP_AASTORE:
+                stack.apop();
+                stack.ipop();
+                stack.apop();
+                break;
+
+            case InstructionConstants.OP_POP:
+                stack.pop1();
+                break;
+
+            case InstructionConstants.OP_POP2:
+                stack.pop2();
+                break;
+
+            case InstructionConstants.OP_DUP:
+                stack.dup();
+                break;
+
+            case InstructionConstants.OP_DUP_X1:
+                stack.dup_x1();
+                break;
+
+            case InstructionConstants.OP_DUP_X2:
+                stack.dup_x2();
+                break;
+
+            case InstructionConstants.OP_DUP2:
+                stack.dup2();
+                break;
+
+            case InstructionConstants.OP_DUP2_X1:
+                stack.dup2_x1();
+                break;
+
+            case InstructionConstants.OP_DUP2_X2:
+                stack.dup2_x2();
+                break;
+
+            case InstructionConstants.OP_SWAP:
+                stack.swap();
+                break;
+
+            case InstructionConstants.OP_IADD:
+                stack.push(stack.ipop().add(stack.ipop()));
+                break;
+
+            case InstructionConstants.OP_LADD:
+                stack.push(stack.lpop().add(stack.lpop()));
+                break;
+
+            case InstructionConstants.OP_FADD:
+                stack.push(stack.fpop().add(stack.fpop()));
+                break;
+
+            case InstructionConstants.OP_DADD:
+                stack.push(stack.dpop().add(stack.dpop()));
+                break;
+
+            case InstructionConstants.OP_ISUB:
+                stack.push(stack.ipop().subtractFrom(stack.ipop()));
+                break;
+
+            case InstructionConstants.OP_LSUB:
+                stack.push(stack.lpop().subtractFrom(stack.lpop()));
+                break;
+
+            case InstructionConstants.OP_FSUB:
+                stack.push(stack.fpop().subtractFrom(stack.fpop()));
+                break;
+
+            case InstructionConstants.OP_DSUB:
+                stack.push(stack.dpop().subtractFrom(stack.dpop()));
+                break;
+
+            case InstructionConstants.OP_IMUL:
+                stack.push(stack.ipop().multiply(stack.ipop()));
+                break;
+
+            case InstructionConstants.OP_LMUL:
+                stack.push(stack.lpop().multiply(stack.lpop()));
+                break;
+
+            case InstructionConstants.OP_FMUL:
+                stack.push(stack.fpop().multiply(stack.fpop()));
+                break;
+
+            case InstructionConstants.OP_DMUL:
+                stack.push(stack.dpop().multiply(stack.dpop()));
+                break;
+
+            case InstructionConstants.OP_IDIV:
+                try
+                {
+                    stack.push(stack.ipop().divideOf(stack.ipop()));
+                }
+                catch (ArithmeticException ex)
+                {
+                    stack.push(IntegerValueFactory.create());
+                    // TODO: Forward ArithmeticExceptions.
+                    //stack.clear();
+                    //stack.push(ReferenceValueFactory.create(false));
+                    //branchUnit.throwException();
+                }
+                break;
+
+            case InstructionConstants.OP_LDIV:
+                stack.push(stack.lpop().divideOf(stack.lpop()));
+                break;
+
+            case InstructionConstants.OP_FDIV:
+                stack.push(stack.fpop().divideOf(stack.fpop()));
+                break;
+
+            case InstructionConstants.OP_DDIV:
+                stack.push(stack.dpop().divideOf(stack.dpop()));
+                break;
+
+            case InstructionConstants.OP_IREM:
+                try
+                {
+                    stack.push(stack.ipop().remainderOf(stack.ipop()));
+                }
+                catch (ArithmeticException ex)
+                {
+                    stack.push(IntegerValueFactory.create());
+                    // TODO: Forward ArithmeticExceptions.
+                    //stack.clear();
+                    //stack.push(ReferenceValueFactory.create(false));
+                    //branchUnit.throwException();
+                }
+                break;
+
+            case InstructionConstants.OP_LREM:
+                stack.push(stack.lpop().remainderOf(stack.lpop()));
+                break;
+
+            case InstructionConstants.OP_FREM:
+                stack.push(stack.fpop().remainderOf(stack.fpop()));
+                break;
+
+            case InstructionConstants.OP_DREM:
+                stack.push(stack.dpop().remainderOf(stack.dpop()));
+                break;
+
+            case InstructionConstants.OP_INEG:
+                stack.push(stack.ipop().negate());
+                break;
+
+            case InstructionConstants.OP_LNEG:
+                stack.push(stack.lpop().negate());
+                break;
+
+            case InstructionConstants.OP_FNEG:
+                stack.push(stack.fpop().negate());
+                break;
+
+            case InstructionConstants.OP_DNEG:
+                stack.push(stack.dpop().negate());
+                break;
+
+            case InstructionConstants.OP_ISHL:
+                stack.push(stack.ipop().shiftLeftOf(stack.ipop()));
+                break;
+
+            case InstructionConstants.OP_LSHL:
+                stack.push(stack.ipop().shiftLeftOf(stack.lpop()));
+                break;
+
+            case InstructionConstants.OP_ISHR:
+                stack.push(stack.ipop().shiftRightOf(stack.ipop()));
+                break;
+
+            case InstructionConstants.OP_LSHR:
+                stack.push(stack.ipop().shiftRightOf(stack.lpop()));
+                break;
+
+            case InstructionConstants.OP_IUSHR:
+                stack.push(stack.ipop().unsignedShiftRightOf(stack.ipop()));
+                break;
+
+            case InstructionConstants.OP_LUSHR:
+                stack.push(stack.ipop().unsignedShiftRightOf(stack.lpop()));
+                break;
+
+            case InstructionConstants.OP_IAND:
+                stack.push(stack.ipop().and(stack.ipop()));
+                break;
+
+            case InstructionConstants.OP_LAND:
+                stack.push(stack.lpop().and(stack.lpop()));
+                break;
+
+            case InstructionConstants.OP_IOR:
+                stack.push(stack.ipop().or(stack.ipop()));
+                break;
+
+            case InstructionConstants.OP_LOR:
+                stack.push(stack.lpop().or(stack.lpop()));
+                break;
+
+            case InstructionConstants.OP_IXOR:
+                stack.push(stack.ipop().xor(stack.ipop()));
+                break;
+
+            case InstructionConstants.OP_LXOR:
+                stack.push(stack.lpop().xor(stack.lpop()));
+                break;
+
+            case InstructionConstants.OP_I2L:
+                stack.push(stack.ipop().convertToLong());
+                break;
+
+            case InstructionConstants.OP_I2F:
+                stack.push(stack.ipop().convertToFloat());
+                break;
+
+            case InstructionConstants.OP_I2D:
+                stack.push(stack.ipop().convertToDouble());
+                break;
+
+            case InstructionConstants.OP_L2I:
+                stack.push(stack.lpop().convertToInteger());
+                break;
+
+            case InstructionConstants.OP_L2F:
+                stack.push(stack.lpop().convertToFloat());
+                break;
+
+            case InstructionConstants.OP_L2D:
+                stack.push(stack.lpop().convertToDouble());
+                break;
+
+            case InstructionConstants.OP_F2I:
+                stack.push(stack.fpop().convertToInteger());
+                break;
+
+            case InstructionConstants.OP_F2L:
+                stack.push(stack.fpop().convertToLong());
+                break;
+
+            case InstructionConstants.OP_F2D:
+                stack.push(stack.fpop().convertToDouble());
+                break;
+
+            case InstructionConstants.OP_D2I:
+                stack.push(stack.dpop().convertToInteger());
+                break;
+
+            case InstructionConstants.OP_D2L:
+                stack.push(stack.dpop().convertToLong());
+                break;
+
+            case InstructionConstants.OP_D2F:
+                stack.push(stack.dpop().convertToFloat());
+                break;
+
+            case InstructionConstants.OP_I2B:
+                stack.push(stack.ipop().convertToByte());
+                break;
+
+            case InstructionConstants.OP_I2C:
+                stack.push(stack.ipop().convertToCharacter());
+                break;
+
+            case InstructionConstants.OP_I2S:
+                stack.push(stack.ipop().convertToShort());
+                break;
+
+            case InstructionConstants.OP_LCMP:
+                stack.push(stack.lpop().compareReverse(stack.lpop()));
+                break;
+
+            case InstructionConstants.OP_FCMPL:
+                FloatValue floatValue1 = stack.fpop();
+                FloatValue floatValue2 = stack.fpop();
+                stack.push(floatValue2.compare(floatValue1));
+                break;
+
+            case InstructionConstants.OP_FCMPG:
+                stack.push(stack.fpop().compareReverse(stack.fpop()));
+                break;
+
+            case InstructionConstants.OP_DCMPL:
+                DoubleValue doubleValue1 = stack.dpop();
+                DoubleValue doubleValue2 = stack.dpop();
+                stack.push(doubleValue2.compare(doubleValue1));
+                break;
+
+            case InstructionConstants.OP_DCMPG:
+                stack.push(stack.dpop().compareReverse(stack.dpop()));
+                break;
+
+            case InstructionConstants.OP_IRETURN:
+                branchUnit.returnFromMethod(stack.ipop());
+                break;
+
+            case InstructionConstants.OP_LRETURN:
+                branchUnit.returnFromMethod(stack.lpop());
+                break;
+
+            case InstructionConstants.OP_FRETURN:
+                branchUnit.returnFromMethod(stack.fpop());
+                break;
+
+            case InstructionConstants.OP_DRETURN:
+                branchUnit.returnFromMethod(stack.dpop());
+                break;
+
+            case InstructionConstants.OP_ARETURN:
+                branchUnit.returnFromMethod(stack.apop());
+                break;
+
+            case InstructionConstants.OP_RETURN:
+                branchUnit.returnFromMethod(null);
+                break;
+
+            case InstructionConstants.OP_NEWARRAY:
+                stack.ipop();
+                stack.push(ReferenceValueFactory.create(false));
+                break;
+
+            case InstructionConstants.OP_ARRAYLENGTH:
+                stack.apop();
+                stack.push(IntegerValueFactory.create());
+                break;
+
+            case InstructionConstants.OP_ATHROW:
+                ReferenceValue exceptionReferenceValue = stack.apop();
+                stack.clear();
+                stack.push(exceptionReferenceValue);
+                branchUnit.throwException();
+                break;
+
+            case InstructionConstants.OP_MONITORENTER:
+            case InstructionConstants.OP_MONITOREXIT:
+                stack.apop();
+                break;
+
+            default:
+                throw new IllegalArgumentException("Unknown simple instruction ["+simpleInstruction.opcode+"]");
+        }
+    }
+
+
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    {
+        int cpIndex = cpInstruction.cpIndex;
+
+        switch (cpInstruction.opcode)
+        {
+            case InstructionConstants.OP_LDC:
+            case InstructionConstants.OP_LDC_W:
+            case InstructionConstants.OP_LDC2_W:
+            case InstructionConstants.OP_GETSTATIC:
+                stack.push(cpValue(classFile, cpIndex));
+                break;
+
+            case InstructionConstants.OP_PUTSTATIC:
+                stack.pop();
+                break;
+
+            case InstructionConstants.OP_GETFIELD:
+                stack.apop();
+                stack.push(cpValue(classFile, cpIndex));
+                break;
+
+            case InstructionConstants.OP_PUTFIELD:
+                stack.pop();
+                stack.apop();
+                break;
+
+            case InstructionConstants.OP_INVOKEVIRTUAL:
+            case InstructionConstants.OP_INVOKESPECIAL:
+            case InstructionConstants.OP_INVOKESTATIC:
+            case InstructionConstants.OP_INVOKEINTERFACE:
+                Value cpValue        = cpValue(classFile, cpIndex);
+                int   parameterCount = parameterCount(classFile, cpIndex);
+
+                for (int counter = 0; counter < parameterCount; counter++)
+                {
+                    stack.pop();
+                }
+                if (cpInstruction.opcode != InstructionConstants.OP_INVOKESTATIC)
+                {
+                    stack.apop();
+                }
+                if (cpValue != null)
+                {
+                    stack.push(cpValue);
+                }
+                break;
+
+            case InstructionConstants.OP_NEW:
+                stack.push(cpValue(classFile, cpIndex));
+                break;
+
+            case InstructionConstants.OP_ANEWARRAY:
+                stack.ipop();
+                stack.push(ReferenceValueFactory.create(referencedClassFile(classFile, cpIndex),
+                                                        1,
+                                                        false));
+                break;
+
+            case InstructionConstants.OP_CHECKCAST:
+                // TODO: Check cast.
+                stack.push(stack.apop());
+                break;
+
+            case InstructionConstants.OP_INSTANCEOF:
+                int instanceOf = stack.apop().instanceOf(referencedClassFile(classFile, cpIndex),
+                                                         referencedTypeDimensionCount(classFile, cpIndex));
+
+                stack.push(instanceOf == Value.NEVER  ? IntegerValueFactory.create(0) :
+                           instanceOf == Value.ALWAYS ? IntegerValueFactory.create(1) :
+                                                        IntegerValueFactory.create());
+                break;
+
+            case InstructionConstants.OP_MULTIANEWARRAY:
+                int dimensionCount = cpInstruction.constant;
+                for (int dimension = 0; dimension < dimensionCount; dimension++)
+                {
+                    stack.ipop();
+                }
+                stack.push(cpValue(classFile, cpIndex));
+                break;
+
+            default:
+                throw new IllegalArgumentException("Unknown constant pool instruction ["+cpInstruction.opcode+"]");
+        }
+    }
+
+
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    {
+        int variableIndex = variableInstruction.variableIndex;
+
+        switch (variableInstruction.opcode)
+        {
+            case InstructionConstants.OP_ILOAD:
+            case InstructionConstants.OP_ILOAD_0:
+            case InstructionConstants.OP_ILOAD_1:
+            case InstructionConstants.OP_ILOAD_2:
+            case InstructionConstants.OP_ILOAD_3:
+                stack.push(variables.iload(variableIndex));
+                break;
+
+            case InstructionConstants.OP_LLOAD:
+            case InstructionConstants.OP_LLOAD_0:
+            case InstructionConstants.OP_LLOAD_1:
+            case InstructionConstants.OP_LLOAD_2:
+            case InstructionConstants.OP_LLOAD_3:
+                stack.push(variables.lload(variableIndex));
+                break;
+
+            case InstructionConstants.OP_FLOAD:
+            case InstructionConstants.OP_FLOAD_0:
+            case InstructionConstants.OP_FLOAD_1:
+            case InstructionConstants.OP_FLOAD_2:
+            case InstructionConstants.OP_FLOAD_3:
+                stack.push(variables.fload(variableIndex));
+                break;
+
+            case InstructionConstants.OP_DLOAD:
+            case InstructionConstants.OP_DLOAD_0:
+            case InstructionConstants.OP_DLOAD_1:
+            case InstructionConstants.OP_DLOAD_2:
+            case InstructionConstants.OP_DLOAD_3:
+                stack.push(variables.dload(variableIndex));
+                break;
+
+            case InstructionConstants.OP_ALOAD:
+            case InstructionConstants.OP_ALOAD_0:
+            case InstructionConstants.OP_ALOAD_1:
+            case InstructionConstants.OP_ALOAD_2:
+            case InstructionConstants.OP_ALOAD_3:
+                stack.push(variables.aload(variableIndex));
+                break;
+
+            case InstructionConstants.OP_ISTORE:
+            case InstructionConstants.OP_ISTORE_0:
+            case InstructionConstants.OP_ISTORE_1:
+            case InstructionConstants.OP_ISTORE_2:
+            case InstructionConstants.OP_ISTORE_3:
+                variables.store(variableIndex, stack.ipop());
+                break;
+
+            case InstructionConstants.OP_LSTORE:
+            case InstructionConstants.OP_LSTORE_0:
+            case InstructionConstants.OP_LSTORE_1:
+            case InstructionConstants.OP_LSTORE_2:
+            case InstructionConstants.OP_LSTORE_3:
+                variables.store(variableIndex, stack.lpop());
+                break;
+
+            case InstructionConstants.OP_FSTORE:
+            case InstructionConstants.OP_FSTORE_0:
+            case InstructionConstants.OP_FSTORE_1:
+            case InstructionConstants.OP_FSTORE_2:
+            case InstructionConstants.OP_FSTORE_3:
+                variables.store(variableIndex, stack.fpop());
+                break;
+
+            case InstructionConstants.OP_DSTORE:
+            case InstructionConstants.OP_DSTORE_0:
+            case InstructionConstants.OP_DSTORE_1:
+            case InstructionConstants.OP_DSTORE_2:
+            case InstructionConstants.OP_DSTORE_3:
+                variables.store(variableIndex, stack.dpop());
+                break;
+
+            case InstructionConstants.OP_ASTORE:
+            case InstructionConstants.OP_ASTORE_0:
+            case InstructionConstants.OP_ASTORE_1:
+            case InstructionConstants.OP_ASTORE_2:
+            case InstructionConstants.OP_ASTORE_3:
+                // The operand on the stack can be a reference or a return
+                // address, so we'll relax the pop operation.
+                //variables.store(variableIndex, stack.apop());
+                variables.store(variableIndex, stack.pop());
+                break;
+
+            case InstructionConstants.OP_IINC:
+                variables.store(variableIndex,
+                                variables.iload(variableIndex).add(
+                                IntegerValueFactory.create(variableInstruction.constant)));
+                break;
+
+            case InstructionConstants.OP_RET:
+                // The return address should be in the last offset of the
+                // given instruction offset variable (even though there may
+                // be other offsets).
+                InstructionOffsetValue instructionOffsetValue = variables.oload(variableIndex);
+                branchUnit.branch(classFile,
+                                  codeAttrInfo,
+                                  offset,
+                                  instructionOffsetValue.instructionOffset(instructionOffsetValue.instructionOffsetCount()-1));
+                break;
+
+            default:
+                throw new IllegalArgumentException("Unknown variable instruction ["+variableInstruction.opcode+"]");
+        }
+    }
+
+
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+    {
+        int branchTarget = offset + branchInstruction.branchOffset;
+
+        switch (branchInstruction.opcode)
+        {
+            case InstructionConstants.OP_IFEQ:
+                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                    stack.ipop().equal(IntegerValueFactory.create(0)));
+                break;
+
+            case InstructionConstants.OP_IFNE:
+                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                    stack.ipop().notEqual(IntegerValueFactory.create(0)));
+                break;
+
+            case InstructionConstants.OP_IFLT:
+                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                    stack.ipop().lessThan(IntegerValueFactory.create(0)));
+                break;
+
+            case InstructionConstants.OP_IFGE:
+                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                    stack.ipop().greaterThanOrEqual(IntegerValueFactory.create(0)));
+                break;
+
+            case InstructionConstants.OP_IFGT:
+                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                    stack.ipop().greaterThan(IntegerValueFactory.create(0)));
+                break;
+
+            case InstructionConstants.OP_IFLE:
+                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                    stack.ipop().lessThanOrEqual(IntegerValueFactory.create(0)));
+                break;
+
+
+            case InstructionConstants.OP_IFICMPEQ:
+                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                    stack.ipop().equal(stack.ipop()));
+                break;
+
+            case InstructionConstants.OP_IFICMPNE:
+                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                    stack.ipop().notEqual(stack.ipop()));
+                break;
+
+
+            case InstructionConstants.OP_IFICMPLT:
+                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                    -stack.ipop().lessThan(stack.ipop()));
+                break;
+
+            case InstructionConstants.OP_IFICMPGE:
+                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                    -stack.ipop().greaterThanOrEqual(stack.ipop()));
+                break;
+
+            case InstructionConstants.OP_IFICMPGT:
+                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                    -stack.ipop().greaterThan(stack.ipop()));
+                break;
+
+            case InstructionConstants.OP_IFICMPLE:
+                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                    -stack.ipop().lessThanOrEqual(stack.ipop()));
+                break;
+
+            case InstructionConstants.OP_IFACMPEQ:
+                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                    stack.apop().equal(stack.apop()));
+                break;
+
+            case InstructionConstants.OP_IFACMPNE:
+                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                    stack.apop().notEqual(stack.apop()));
+                break;
+
+            case InstructionConstants.OP_GOTO:
+            case InstructionConstants.OP_GOTO_W:
+                branchUnit.branch(classFile, codeAttrInfo, offset, branchTarget);
+                break;
+
+
+            case InstructionConstants.OP_JSR:
+            case InstructionConstants.OP_JSR_W:
+                stack.push(InstructionOffsetValueFactory.create(offset +
+                                                                branchInstruction.length(offset)));
+                branchUnit.branch(classFile, codeAttrInfo, offset, branchTarget);
+                break;
+
+            case InstructionConstants.OP_IFNULL:
+                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                    stack.apop().isNull());
+                break;
+
+            case InstructionConstants.OP_IFNONNULL:
+                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                    stack.apop().isNotNull());
+                break;
+
+            default:
+                throw new IllegalArgumentException("Unknown branch instruction ["+branchInstruction.opcode+"]");
+        }
+    }
+
+
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
+    {
+        IntegerValue indexValue = stack.ipop();
+
+        // If there is no definite branch in any of the cases below,
+        // branch to the default offset.
+        branchUnit.branch(classFile, codeAttrInfo,
+                          offset,
+                          offset + tableSwitchInstruction.defaultOffset);
+
+        for (int index = 0; index < tableSwitchInstruction.jumpOffsetCount; index++)
+        {
+            int conditional = indexValue.equal(IntegerValueFactory.create(
+                tableSwitchInstruction.lowCase + index));
+            branchUnit.branchConditionally(classFile, codeAttrInfo,
+                                           offset,
+                                           offset + tableSwitchInstruction.jumpOffsets[index],
+                                           conditional);
+
+            // If this branch is always taken, we can skip the rest.
+            if (conditional == Value.ALWAYS)
+            {
+                break;
+            }
+        }
+    }
+
+
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
+    {
+        IntegerValue indexValue = stack.ipop();
+
+        // If there is no definite branch in any of the cases below,
+        // branch to the default offset.
+        branchUnit.branch(classFile, codeAttrInfo,
+                          offset,
+                          offset + lookUpSwitchInstruction.defaultOffset);
+
+        for (int index = 0; index < lookUpSwitchInstruction.jumpOffsetCount; index++)
+        {
+            int conditional = indexValue.equal(IntegerValueFactory.create(
+                lookUpSwitchInstruction.cases[index]));
+            branchUnit.branchConditionally(classFile, codeAttrInfo,
+                                           offset,
+                                           offset + lookUpSwitchInstruction.jumpOffsets[index],
+                                           conditional);
+
+            // If this branch is always taken, we can skip the rest.
+            if (conditional == Value.ALWAYS)
+            {
+                break;
+            }
+        }
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo)
+    {
+        cpValue = IntegerValueFactory.create(integerCpInfo.getValue());
+    }
+
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo)
+    {
+        cpValue = LongValueFactory.create(longCpInfo.getValue());
+    }
+
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo)
+    {
+        cpValue = FloatValueFactory.create(floatCpInfo.getValue());
+    }
+
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo)
+    {
+        cpValue = DoubleValueFactory.create(doubleCpInfo.getValue());
+    }
+
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
+    {
+        cpValue = ReferenceValueFactory.create(false);
+    }
+
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    {
+        cpValue = ValueFactory.create(fieldrefCpInfo.getType(classFile));
+    }
+
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    {
+        visitRefCpInfo(classFile, interfaceMethodrefCpInfo);
+    }
+
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    {
+        visitRefCpInfo(classFile, methodrefCpInfo);
+    }
+
+    private void visitRefCpInfo(ClassFile classFile, RefCpInfo methodrefCpInfo)
+    {
+        String type = methodrefCpInfo.getType(classFile);
+
+        parameterCount = ClassUtil.internalMethodParameterCount(type);
+        cpValue        = ValueFactory.create(ClassUtil.internalMethodReturnType(type));
+    }
+
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    {
+        String className = classCpInfo.getName(classFile);
+
+        referencedClassFile          = classCpInfo.referencedClassFile;
+        referencedTypeDimensionCount = ClassUtil.internalArrayTypeDimensionCount(className);
+
+        cpValue = ReferenceValueFactory.create(referencedClassFile,
+                                               referencedTypeDimensionCount,
+                                               false);
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns the Value of the constant pool element at the given index.
+     * The element can be a constant, a field, a method,...
+     */
+    private Value cpValue(ClassFile classFile, int cpIndex)
+    {
+        // Visit the constant pool entry to get its return value.
+        classFile.constantPoolEntryAccept(cpIndex, this);
+
+        return cpValue;
+    }
+
+
+    /**
+     * Returns the class file referenced by the class constant pool entry at the
+     * given index.
+     */
+    private ClassFile referencedClassFile(ClassFile classFile, int cpIndex)
+    {
+        // Visit the constant pool entry to get its referenced class file.
+        classFile.constantPoolEntryAccept(cpIndex, this);
+
+        return referencedClassFile;
+    }
+
+
+    /**
+     * Returns the dimensionality of the class constant pool entry at the given
+     * index.
+     */
+    private int referencedTypeDimensionCount(ClassFile classFile, int cpIndex)
+    {
+        // Visit the constant pool entry to get its referenced class file.
+        //classFile.constantPoolEntryAccept(this, cpIndex);
+
+        // We'll return the value that was just computed.
+        return referencedTypeDimensionCount;
+    }
+
+
+    /**
+     * Returns the number of parameters of the method reference at the given
+     * constant pool index. This method must be invoked right after the
+     * cpValue(ClassFile,int) method.
+     */
+    private int parameterCount(ClassFile classFile, int methodRefCpIndex)
+    {
+        // Visit the method ref constant pool entry to get its parameter count.
+        //classFile.constantPoolEntryAccept(this, methodRefCpIndex);
+
+        // We'll return the value that was just computed.
+        return parameterCount;
+    }
+}
diff --git a/src/proguard/optimize/evaluation/Stack.java b/src/proguard/optimize/evaluation/Stack.java
new file mode 100644
index 0000000..22b477e
--- /dev/null
+++ b/src/proguard/optimize/evaluation/Stack.java
@@ -0,0 +1,515 @@
+/* $Id: Stack.java,v 1.6 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation;
+
+import proguard.optimize.evaluation.value.*;
+
+/**
+ * This class represents an operand stack that contains <code>Value</code>
+ * objects.
+ *
+ * @author Eric Lafortune
+ */
+class Stack
+{
+    protected Value[] values;
+    protected int     currentSize;
+    protected int     actualMaxSize;
+
+
+    /**
+     * Creates a new Stack with a given maximum size, accounting for the double
+     * space required by Category 2 values.
+     */
+    public Stack(int maxSize)
+    {
+        values = new Value[maxSize];
+    }
+
+
+    /**
+     * Creates a Stack that is a copy of the given Stack.
+     */
+    public Stack(Stack stack)
+    {
+        // Create the values array.
+        this(stack.values.length);
+
+        // Copy the stack contents.
+        copy(stack);
+    }
+
+
+    /**
+     * Returns the actual maximum stack size that was required for all stack
+     * operations, accounting for the double space required by Category 2 values.
+     */
+    public int getActualMaxSize()
+    {
+        return actualMaxSize;
+    }
+
+
+    /**
+     * Resets this Stack, so that it can be reused.
+     */
+    public void reset(int maxSize)
+    {
+        // Is the values array large enough?
+        if (maxSize > values.length)
+        {
+            // Create a new one.
+            values = new Value[maxSize];
+        }
+
+        // Clear the sizes.
+        clear();
+
+        actualMaxSize = 0;
+    }
+
+
+    /**
+     * Copies the values of the given Stack into this Stack.
+     */
+    public void copy(Stack other)
+    {
+        // Is the values array large enough?
+        if (other.values.length > values.length)
+        {
+            // Create a new one.
+            values = new Value[other.values.length];
+        }
+
+        // Copy the stack contents.
+        System.arraycopy(other.values, 0, this.values, 0, other.currentSize);
+
+        // Copy the sizes.
+        currentSize   = other.currentSize;
+        actualMaxSize = other.actualMaxSize;
+    }
+
+
+    /**
+     * Generalizes the values of this Stack with the values of the given Stack.
+     * The stacks must have the same current sizes.
+     * @return whether the generalization has made any difference.
+     */
+    public boolean generalize(Stack other)
+    {
+        if (this.currentSize != other.currentSize)
+        {
+            throw new IllegalArgumentException("Stacks have different current sizes ["+this.currentSize+"] and ["+other.currentSize+"]");
+        }
+
+        boolean changed = false;
+
+        // Generalize the stack values.
+        for (int index = 0; index < currentSize; index++)
+        {
+            Value otherValue = other.values[index];
+
+            if (otherValue != null)
+            {
+                Value thisValue  = this.values[index];
+
+                if (thisValue != null)
+                {
+                    otherValue = thisValue.generalize(otherValue);
+
+                    changed = changed || !otherValue.equals(thisValue);
+                }
+
+                values[index] = otherValue;
+            }
+        }
+
+        // Check if the other stack extends beyond this one.
+        if (this.actualMaxSize < other.actualMaxSize)
+        {
+            this.actualMaxSize = other.actualMaxSize;
+        }
+
+        return changed;
+    }
+
+
+    /**
+     * Clears the stack.
+     */
+    public void clear()
+    {
+        // Clear the stack contents.
+        for (int index = 0; index < currentSize; index++)
+        {
+            values[index] = null;
+        }
+
+        currentSize = 0;
+    }
+
+
+    /**
+     * Returns the number of elements currently on the stack, accounting for the
+     * double space required by Category 2 values..
+     */
+    public int size()
+    {
+        return currentSize;
+    }
+
+
+    /**
+     * Gets the specified Value from the stack, without disturbing it.
+     * @param index the index of the stack element, counting from the bottom
+     *              of the stack.
+     */
+    public Value getBottom(int index)
+    {
+        return values[index];
+    }
+
+
+    /**
+     * Gets the specified Value from the stack, without disturbing it.
+     * @param index the index of the stack element, counting from the top
+     *              of the stack.
+     */
+    public Value getTop(int index)
+    {
+        return values[currentSize - index - 1];
+    }
+
+
+    /**
+     * Pushes the given Value onto the stack.
+     */
+    public void push(Value value)
+    {
+        // Account for the extra space required by Category 2 values.
+        if (value.isCategory2())
+        {
+            currentSize++;
+        }
+
+        // Push the value.
+        values[currentSize++] = value;
+
+        // Update the maximum actual size;
+        if (actualMaxSize < currentSize)
+        {
+            actualMaxSize = currentSize;
+        }
+    }
+
+
+    /**
+     * Pops the top Value from the stack.
+     */
+    public Value pop()
+    {
+        Value value = values[--currentSize];
+
+        values[currentSize] = null;
+
+        // Account for the extra space required by Category 2 values.
+        if (value.isCategory2())
+        {
+            values[--currentSize] = null;
+        }
+
+        return value;
+    }
+
+
+    // Pop methods that provide convenient casts to the expected value types.
+
+    /**
+     * Pops the top IntegerValue from the stack.
+     */
+    public IntegerValue ipop()
+    {
+        return pop().integerValue();
+    }
+
+
+    /**
+     * Pops the top LongValue from the stack.
+     */
+    public LongValue lpop()
+    {
+        return pop().longValue();
+    }
+
+
+    /**
+     * Pops the top FloatValue from the stack.
+     */
+    public FloatValue fpop()
+    {
+        return pop().floatValue();
+    }
+
+
+    /**
+     * Pops the top DoubleValue from the stack.
+     */
+    public DoubleValue dpop()
+    {
+        return pop().doubleValue();
+    }
+
+
+    /**
+     * Pops the top ReferenceValue from the stack.
+     */
+    public ReferenceValue apop()
+    {
+        return pop().referenceValue();
+    }
+
+
+    /**
+     * Pops the top InstructionOffsetValue from the stack.
+     */
+    public InstructionOffsetValue opop()
+    {
+        return pop().instructionOffsetValue();
+    }
+
+
+    /**
+     * Pops the top category 1 value from the stack.
+     */
+    public void pop1()
+    {
+        values[--currentSize] = null;
+    }
+
+
+    /**
+     * Pops the top category 2 value from the stack (or alternatively, two
+     * Category 1 stack elements).
+     */
+    public void pop2()
+    {
+        values[--currentSize] = null;
+        values[--currentSize] = null;
+    }
+
+
+    /**
+     * Duplicates the top Category 1 value.
+     */
+    public void dup()
+    {
+        values[currentSize] = values[currentSize - 1].category1Value();
+
+        currentSize++;
+
+        // Update the maximum actual size;
+        if (actualMaxSize < currentSize)
+        {
+            actualMaxSize = currentSize;
+        }
+    }
+
+
+    /**
+     * Duplicates the top Category 1 value, one Category 1 element down the
+     * stack.
+     */
+    public void dup_x1()
+    {
+        values[currentSize]     = values[currentSize - 1].category1Value();
+        values[currentSize - 1] = values[currentSize - 2].category1Value();
+        values[currentSize - 2] = values[currentSize    ];
+
+        currentSize++;
+
+        // Update the maximum actual size;
+        if (actualMaxSize < currentSize)
+        {
+            actualMaxSize = currentSize;
+        }
+    }
+
+
+    /**
+     * Duplicates the top Category 1 value, two Category 1 elements (or one
+     * Category 2 element) down the stack.
+     */
+    public void dup_x2()
+    {
+        values[currentSize]     = values[currentSize - 1].category1Value();
+        values[currentSize - 1] = values[currentSize - 2];
+        values[currentSize - 2] = values[currentSize - 3];
+        values[currentSize - 3] = values[currentSize    ];
+
+        currentSize++;
+
+        // Update the maximum actual size;
+        if (actualMaxSize < currentSize)
+        {
+            actualMaxSize = currentSize;
+        }
+    }
+
+    /**
+     * Duplicates the top Category 2 value (or alternatively, the equivalent
+     * Category 1 stack elements).
+     */
+    public void dup2()
+    {
+        values[currentSize    ] = values[currentSize - 2];
+        values[currentSize + 1] = values[currentSize - 1];
+
+        currentSize += 2;
+
+        // Update the maximum actual size;
+        if (actualMaxSize < currentSize)
+        {
+            actualMaxSize = currentSize;
+        }
+    }
+
+
+    /**
+     * Duplicates the top Category 2 value, one Category 1 element down the
+     * stack (or alternatively, the equivalent Category 1 stack values).
+     */
+    public void dup2_x1()
+    {
+        values[currentSize + 1] = values[currentSize - 1];
+        values[currentSize    ] = values[currentSize - 2];
+        values[currentSize - 1] = values[currentSize - 3];
+        values[currentSize - 2] = values[currentSize + 1];
+        values[currentSize - 3] = values[currentSize    ];
+
+        currentSize += 2;
+
+        // Update the maximum actual size;
+        if (actualMaxSize < currentSize)
+        {
+            actualMaxSize = currentSize;
+        }
+    }
+
+
+    /**
+     * Duplicates the top Category 2 value, one Category 2 stack element down
+     * the stack (or alternatively, the equivalent Category 1 stack values).
+     */
+    public void dup2_x2()
+    {
+        values[currentSize + 1] = values[currentSize - 1];
+        values[currentSize    ] = values[currentSize - 2];
+        values[currentSize - 1] = values[currentSize - 3];
+        values[currentSize - 2] = values[currentSize - 4];
+        values[currentSize - 3] = values[currentSize + 1];
+        values[currentSize - 4] = values[currentSize    ];
+
+        currentSize += 2;
+
+        // Update the maximum actual size;
+        if (actualMaxSize < currentSize)
+        {
+            actualMaxSize = currentSize;
+        }
+    }
+
+
+    /**
+     * Swaps the top two Category 1 values.
+     */
+    public void swap()
+    {
+        Value value1 = values[currentSize - 1].category1Value();
+        Value value2 = values[currentSize - 2].category1Value();
+
+        values[currentSize - 1] = value2;
+        values[currentSize - 2] = value1;
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (this.getClass() != object.getClass())
+        {
+            return false;
+        }
+
+        Stack other = (Stack)object;
+
+        if (this.currentSize != other.currentSize)
+        {
+            return false;
+        }
+
+        for (int index = 0; index < currentSize; index++)
+        {
+            Value thisValue  = this.values[index];
+            Value otherValue = other.values[index];
+            if (thisValue == null ? otherValue != null :
+                                    !thisValue.equals(otherValue))
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+    public int hashCode()
+    {
+        int hashCode = currentSize;
+
+        for (int index = 0; index < currentSize; index++)
+        {
+            Value value = values[index];
+            if (value != null)
+            {
+                hashCode ^= value.hashCode();
+            }
+        }
+
+        return hashCode;
+    }
+
+
+    public String toString()
+    {
+        StringBuffer buffer = new StringBuffer();
+
+        for (int index = 0; index < currentSize; index++)
+        {
+            Value value = values[index];
+            buffer = buffer.append('[')
+                           .append(value == null ? "empty" : value.toString())
+                           .append(']');
+        }
+
+        return buffer.toString();
+    }
+}
diff --git a/src/proguard/optimize/evaluation/TracedBranchUnit.java b/src/proguard/optimize/evaluation/TracedBranchUnit.java
new file mode 100644
index 0000000..54c922c
--- /dev/null
+++ b/src/proguard/optimize/evaluation/TracedBranchUnit.java
@@ -0,0 +1,146 @@
+/* $Id: TracedBranchUnit.java,v 1.3 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.optimize.evaluation.value.*;
+
+/**
+ * This BranchUnit remembers the branch unit commands that are invoked on it.
+ *
+ * @author Eric Lafortune
+ */
+class TracedBranchUnit implements BranchUnit
+{
+    private static final boolean DEBUG = true;
+
+    private boolean                wasCalled;
+    private InstructionOffsetValue traceBranchTargets;
+    private Value                  traceReturnValue;
+
+
+    /**
+     * Resets the flag that tells whether any of the branch unit commands was
+     * called.
+     */
+    public void resetCalled()
+    {
+        wasCalled = false;
+    }
+
+    /**
+     * Returns whether any of the branch unit commands was called.
+     */
+    public boolean wasCalled()
+    {
+        return wasCalled;
+    }
+
+
+    /**
+     * Sets the initial branch targets, which will be updated as the branch
+     * methods of the branch unit are called.
+     */
+    public void setTraceBranchTargets(InstructionOffsetValue branchTargets)
+    {
+        this.traceBranchTargets = branchTargets;
+    }
+
+    public InstructionOffsetValue getTraceBranchTargets()
+    {
+        return traceBranchTargets;
+    }
+
+
+    /**
+     * Sets the initial return Value, which will be generalized as the
+     * return method of the branch unit is called. The initial value may be
+     * null.
+     */
+    public void setTraceReturnValue(Value traceReturnValue)
+    {
+        this.traceReturnValue = traceReturnValue;
+    }
+
+    public Value getTraceReturnValue()
+    {
+        return traceReturnValue;
+    }
+
+
+    // Implementations for BranchUnit.
+
+    public void branch(ClassFile    classFile,
+                       CodeAttrInfo codeAttrInfo,
+                       int          offset,
+                       int          branchTarget)
+    {
+        branchConditionally(classFile,
+                            codeAttrInfo,
+                            offset,
+                            branchTarget,
+                            Value.ALWAYS);
+    }
+
+
+    public void branchConditionally(ClassFile    classFile,
+                                    CodeAttrInfo codeAttrInfo,
+                                    int          offset,
+                                    int          branchTarget,
+                                    int          conditional)
+    {
+        // Mark possible branches at the offset and at the branch target.
+        if (conditional != Value.NEVER)
+        {
+            InstructionOffsetValue branchTargetValue = InstructionOffsetValueFactory.create(branchTarget);
+
+            // Accumulate the branch targets for the evaluator.
+            traceBranchTargets = conditional == Value.ALWAYS ?
+                branchTargetValue :
+                traceBranchTargets.generalize(branchTargetValue).instructionOffsetValue();
+        }
+
+        wasCalled = true;
+    }
+
+
+    public void returnFromMethod(Value returnValue)
+    {
+        traceReturnValue = traceReturnValue == null ?
+            returnValue :
+            traceReturnValue.generalize(returnValue);
+
+        // Stop processing this block.
+        traceBranchTargets = InstructionOffsetValueFactory.create();
+
+        wasCalled = true;
+    }
+
+
+    public void throwException()
+    {
+        // Stop processing this block.
+        traceBranchTargets = InstructionOffsetValueFactory.create();
+
+        wasCalled = true;
+    }
+}
diff --git a/src/proguard/optimize/evaluation/TracedStack.java b/src/proguard/optimize/evaluation/TracedStack.java
new file mode 100644
index 0000000..964a95b
--- /dev/null
+++ b/src/proguard/optimize/evaluation/TracedStack.java
@@ -0,0 +1,336 @@
+/* $Id: TracedStack.java,v 1.7 2004/11/20 15:06:55 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation;
+
+import proguard.optimize.evaluation.value.*;
+
+/**
+ * This Stack saves a given store Value along with each Value it stores, and
+ * at the same time generalizes a given trace Value with the store Value of
+ * each Value it loads. The store Value and the trace Value can be set; the
+ * generalized trace Value can be retrieved. The object is to store additional
+ * information along with the actual stack values, for instance to keep track
+ * of their origins.
+ *
+ * @author Eric Lafortune
+ */
+class TracedStack extends Stack
+{
+    private Stack traceStack;
+    private Value storeValue;
+    private Value traceValue;
+
+
+    public TracedStack(int maxSize)
+    {
+        super(maxSize);
+
+        traceStack = new Stack(maxSize);
+    }
+
+
+    public TracedStack(TracedStack tracedStack)
+    {
+        super(tracedStack);
+
+        traceStack = new Stack(tracedStack.traceStack);
+    }
+
+
+    /**
+     * Sets the Value that will be stored along with all store instructions.
+     */
+    public void setStoreValue(Value storeValue)
+    {
+        this.storeValue = storeValue;
+    }
+
+
+    /**
+     * Sets the initial Value with which all values stored along with load
+     * instructions will be generalized.
+     */
+    public void setTraceValue(Value traceValue)
+    {
+        this.traceValue = traceValue;
+    }
+
+    public Value getTraceValue()
+    {
+        return traceValue;
+    }
+
+
+    /**
+     * Gets the specified trace Value from the stack, without disturbing it.
+     * @param index the index of the stack element, counting from the bottom
+     *              of the stack.
+     */
+    public Value getBottomTraceValue(int index)
+    {
+        return traceStack.getBottom(index);
+    }
+
+
+    /**
+     * Gets the specified trace Value from the stack, without disturbing it.
+     * @param index the index of the stack element, counting from the top
+     *              of the stack.
+     */
+    public Value getTopTraceValue(int index)
+    {
+        return traceStack.getTop(index);
+    }
+
+
+    // Implementations for Stack.
+
+    public void reset(int size)
+    {
+        super.reset(size);
+
+        traceStack.reset(size);
+    }
+
+    public void copy(TracedStack other)
+    {
+        super.copy(other);
+
+        traceStack.copy(other.traceStack);
+    }
+
+    public boolean generalize(TracedStack other)
+    {
+        return
+            super.generalize(other) |
+            traceStack.generalize(other.traceStack);
+    }
+
+    public void clear()
+    {
+        super.clear();
+
+        traceStack.clear();
+    }
+
+    public void push(Value value)
+    {
+        super.push(value);
+
+        tracePush();
+
+        // Account for the extra space required by Category 2 values.
+        if (value.isCategory2())
+        {
+            tracePush();
+        }
+    }
+
+    public Value pop()
+    {
+        Value value = super.pop();
+
+        tracePop();
+
+        // Account for the extra space required by Category 2 values.
+        if (value.isCategory2())
+        {
+            tracePop();
+        }
+
+        return value;
+    }
+
+    public void pop1()
+    {
+        super.pop1();
+
+        tracePop();
+    }
+
+    public void pop2()
+    {
+        super.pop2();
+
+        tracePop();
+        tracePop();
+    }
+
+    public void dup()
+    {
+        super.dup();
+
+        // For now, we're letting all stack values that are somehow involved
+        // depend on this instruction.
+        Value tracePopValue = tracePop();
+
+        tracePush();
+        traceStack.push(tracePopValue);
+    }
+
+    public void dup_x1()
+    {
+        super.dup_x1();
+
+        // Let the duplicated value depend on this instruction.
+        Value tracePopValue  = tracePop();
+        Value traceSkipValue = traceStack.pop();
+
+        tracePush();
+        traceStack.push(traceSkipValue);
+        traceStack.push(tracePopValue);
+    }
+
+    public void dup_x2()
+    {
+        super.dup_x2();
+
+        // Let the duplicated value depend on this instruction.
+        Value tracePopValue   = tracePop();
+        Value traceSkipValue1 = traceStack.pop();
+        Value traceSkipValue2 = traceStack.pop();
+
+        tracePush();
+        traceStack.push(traceSkipValue2);
+        traceStack.push(traceSkipValue1);
+        traceStack.push(tracePopValue);
+    }
+
+    public void dup2()
+    {
+        super.dup2();
+
+        // Let the duplicated value depend on this instruction.
+        Value tracePopValue1  = tracePop();
+        Value tracePopValue2  = tracePop();
+
+        tracePush();
+        tracePush();
+        traceStack.push(tracePopValue2);
+        traceStack.push(tracePopValue1);
+    }
+
+    public void dup2_x1()
+    {
+        super.dup2_x1();
+
+        // Let the duplicated value depend on this instruction.
+        Value tracePopValue1 = tracePop();
+        Value tracePopValue2 = tracePop();
+        Value traceSkipValue = traceStack.pop();
+
+        tracePush();
+        tracePush();
+        traceStack.push(traceSkipValue);
+        traceStack.push(tracePopValue2);
+        traceStack.push(tracePopValue1);
+    }
+
+    public void dup2_x2()
+    {
+        super.dup2_x2();
+
+        // Let the duplicated value depend on this instruction.
+        Value tracePopValue1  = tracePop();
+        Value tracePopValue2  = tracePop();
+        Value traceSkipValue1 = traceStack.pop();
+        Value traceSkipValue2 = traceStack.pop();
+
+        tracePush();
+        tracePush();
+        traceStack.push(traceSkipValue2);
+        traceStack.push(traceSkipValue1);
+        traceStack.push(tracePopValue2);
+        traceStack.push(tracePopValue1);
+    }
+
+    public void swap()
+    {
+        super.swap();
+
+        // Let one of the swapped values depend on this instruction.
+        tracePop();
+        Value traceSwapValue = traceStack.pop();
+
+        tracePush();
+        traceStack.push(traceSwapValue);
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (this.getClass() != object.getClass())
+        {
+            return false;
+        }
+
+        TracedStack other = (TracedStack)object;
+
+        return super.equals(object) && this.traceStack.equals(other.traceStack);
+    }
+
+
+    public int hashCode()
+    {
+        return super.hashCode() ^ traceStack.hashCode();
+    }
+
+
+    public String toString()
+    {
+        StringBuffer buffer = new StringBuffer();
+
+        for (int index = 0; index < this.size(); index++)
+        {
+            Value value       = this.values[index];
+            Value tracedValue = traceStack.values[index];
+            buffer = buffer.append('[')
+                           .append(tracedValue == null ? "empty" : tracedValue.toString())
+                           .append('>')
+                           .append(value       == null ? "empty" : value.toString())
+                           .append(']');
+        }
+
+        return buffer.toString();
+    }
+
+
+    // Small utility methods.
+
+    private void tracePush()
+    {
+        traceStack.push(storeValue);
+    }
+
+
+    private Value tracePop()
+    {
+        Value popTraceValue = traceStack.pop();
+        if (traceValue != null)
+        {
+            traceValue = traceValue.generalize(popTraceValue);
+        }
+
+        return popTraceValue;
+    }
+}
diff --git a/src/proguard/optimize/evaluation/TracedVariables.java b/src/proguard/optimize/evaluation/TracedVariables.java
new file mode 100644
index 0000000..77a5a33
--- /dev/null
+++ b/src/proguard/optimize/evaluation/TracedVariables.java
@@ -0,0 +1,191 @@
+/* $Id: TracedVariables.java,v 1.7 2004/11/20 15:06:55 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation;
+
+import proguard.optimize.evaluation.value.*;
+
+/**
+ * This Variables class saves a given store Value along with each Value it
+ * stores, and at the same time generalizes a given trace Value with the store
+ * Value of each Value it loads. The store Value and the trace Value can be set;
+ * the generalized trace Value can be retrieved. The object is to store
+ * additional information along with the actual variable values, for instance
+ * to keep track of their origins.
+ * <p>
+ * In addition, a boolean initialization flag can be reset and retrieved,
+ * indicating whether store operations on a variable may have initialized the
+ * variable.
+ *
+ * @author Eric Lafortune
+ */
+class TracedVariables extends Variables
+{
+    private Variables traceVariables;
+    private Value     storeValue;
+    private Value     traceValue;
+    private boolean   initialization;
+
+
+    public TracedVariables(int size)
+    {
+        super(size);
+
+        traceVariables = new Variables(size);
+    }
+
+
+    public TracedVariables(TracedVariables tracedVariables)
+    {
+        super(tracedVariables);
+
+        traceVariables = new Variables(tracedVariables.traceVariables);
+    }
+
+
+    /**
+     * Sets the Value that will be stored along with all store instructions.
+     */
+    public void setStoreValue(Value storeValue)
+    {
+        this.storeValue = storeValue;
+    }
+
+
+    /**
+     * Sets the initial Value with which all values stored along with load
+     * instructions will be generalized.
+     */
+    public void setTraceValue(Value traceValue)
+    {
+        this.traceValue = traceValue;
+    }
+
+    public Value getTraceValue()
+    {
+        return traceValue;
+    }
+
+
+    /**
+     * Resets the initialization flag.
+     */
+    public void resetInitialization()
+    {
+        initialization = false;
+    }
+
+    public boolean wasInitialization()
+    {
+        return initialization;
+    }
+
+
+    // Implementations for Variables.
+
+    public void reset(int size)
+    {
+        super.reset(size);
+
+        traceVariables.reset(size);
+    }
+
+    public void initialize(TracedVariables other)
+    {
+        super.initialize(other);
+
+        traceVariables.initialize(other.traceVariables);
+    }
+
+    public boolean generalize(TracedVariables other)
+    {
+        return
+            super.generalize(other) |
+            traceVariables.generalize(other.traceVariables);
+    }
+
+    public void store(int index, Value value)
+    {
+        // Is this store operation an initialization of the variable?
+        Value previousValue = super.load(index);
+        initialization =
+            initialization        ||
+            previousValue == null ||
+            previousValue.computationalType() != value.computationalType();
+
+        // Store the value itself in the variable.
+        super.store(index, value);
+
+        // Store the store value in its trace variable.
+        traceVariables.store(index, storeValue);
+    }
+
+    public Value load(int index)
+    {
+        // Load and accumulate the store value of the variable.
+        if (traceValue != null)
+        {
+            traceValue = traceValue.generalize(traceVariables.load(index));
+        }
+
+        // Return the value itself.
+        return super.load(index);
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (this.getClass() != object.getClass())
+        {
+            return false;
+        }
+
+        TracedVariables other = (TracedVariables)object;
+
+        return super.equals(object) && this.traceVariables.equals(other.traceVariables);
+    }
+
+
+    public int hashCode()
+    {
+        return super.hashCode() ^ traceVariables.hashCode();
+    }
+
+
+    public String toString()
+    {
+        StringBuffer buffer = new StringBuffer();
+
+        for (int index = 0; index < this.size(); index++)
+        {
+            Value value       = this.values[index];
+            Value tracedValue = traceVariables.values[index];
+            buffer = buffer.append('[')
+                           .append(tracedValue == null ? "empty" : tracedValue.toString())
+                           .append('>')
+                           .append(value       == null ? "empty" : value.toString())
+                           .append(']');
+        }
+
+        return buffer.toString();
+    }
+}
diff --git a/src/proguard/optimize/evaluation/Variables.java b/src/proguard/optimize/evaluation/Variables.java
new file mode 100644
index 0000000..12070c7
--- /dev/null
+++ b/src/proguard/optimize/evaluation/Variables.java
@@ -0,0 +1,312 @@
+/* $Id: Variables.java,v 1.6 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation;
+
+import proguard.optimize.evaluation.value.*;
+
+/**
+ * This class represents a local variable frame that contains <code>Value</code>
+ * objects. Values are generalizations of all values that have been stored in
+ * the respective variables.
+ *
+ * @author Eric Lafortune
+ */
+class Variables
+{
+    protected Value[] values;
+    protected int     size;
+
+
+    /**
+     * Creates a new Variables object with a given maximum number of variables.
+     */
+    public Variables(int size)
+    {
+        this.values = new Value[size];
+        this.size   = size;
+    }
+
+
+    /**
+     * Creates a Variables object that is a copy of the given Variables object.
+     */
+    public Variables(Variables variables)
+    {
+        // Create the values array.
+        this(variables.size);
+
+        // Copy the values.
+        initialize(variables);
+    }
+
+
+    /**
+     * Resets this Variables object, so that it can be reused.
+     */
+    public void reset(int size)
+    {
+        // Is the values array large enough?
+        if (size > values.length)
+        {
+            // Create a new one.
+            values = new Value[size];
+        }
+        else
+        {
+            // Clear the variables.
+            for (int index = 0; index < values.length; index++)
+            {
+                values[index] = null;
+            }
+        }
+
+        this.size = size;
+    }
+
+
+    /**
+     * Initializes the values of this Variables object with the values of the
+     * given Variables object. The other object may have fewer values, in which
+     * case the remaining values are left unchanged.
+     */
+    public void initialize(Variables other)
+    {
+        if (this.size < other.size)
+        {
+            throw new IllegalArgumentException("Variable frame is too small ["+this.size+"] compared to other frame ["+other.size+"]");
+        }
+
+        // Copy the values.
+        System.arraycopy(other.values, 0, this.values, 0, other.size);
+    }
+
+
+    /**
+     * Generalizes the values of this Variables object with the values of the
+     * given Variables object. In case of conflicts, the other Variables
+     * object gets precedence.
+     * @return whether the generalization has made any difference.
+     */
+    public boolean generalize(Variables other)
+    {
+        if (this.size != other.size)
+        {
+            throw new IllegalArgumentException("Variable frames have different sizes ["+this.size+"] and ["+other.size+"]");
+        }
+
+        boolean changed = false;
+
+        for (int index = 0; index < size; index++)
+        {
+            Value otherValue = other.values[index];
+
+            if (otherValue != null)
+            {
+                Value thisValue  = this.values[index];
+
+                // Occasionally, two values of different types might be
+                // present in the same variable in a variable frame
+                // (corresponding to two local variables that share the
+                // same index), at some point outside of their scopes.
+                // The new value gets precedence.
+                if (thisValue != null &&
+                    thisValue.computationalType() == otherValue.computationalType())
+                {
+                    otherValue = thisValue.generalize(otherValue);
+
+                    changed = changed || !otherValue.equals(thisValue);
+                }
+
+                values[index] = otherValue;
+            }
+        }
+
+        return changed;
+    }
+
+
+    /**
+     * Returns the number of variables.
+     */
+    public int size()
+    {
+        return size;
+    }
+
+
+    /**
+     * Stores the given Value at the given variable index.
+     */
+    public void store(int index, Value value)
+    {
+        if (index < 0 ||
+            index >= size)
+        {
+            throw new IndexOutOfBoundsException("Variable index ["+index+"] out of bounds ["+size+"]");
+        }
+
+        // Store the value.
+        values[index] = value;
+    }
+
+
+    /**
+     * Loads the Value from the variable with the given index.
+     */
+    public Value load(int index)
+    {
+        if (index < 0 ||
+            index >= size)
+        {
+            throw new IndexOutOfBoundsException("Variable index ["+index+"] out of bounds ["+size+"]");
+        }
+
+        return values[index];
+    }
+
+
+    // Load methods that provide convenient casts to the expected value types.
+
+    /**
+     * Loads the IntegerValue from the variable with the given index.
+     */
+    public IntegerValue iload(int index)
+    {
+        return load(index).integerValue();
+    }
+
+
+    /**
+     * Loads the LongValue from the variable with the given index.
+     */
+    public LongValue lload(int index)
+    {
+        return load(index).longValue();
+    }
+
+
+    /**
+     * Loads the FloatValue from the variable with the given index.
+     */
+    public FloatValue fload(int index)
+    {
+        return load(index).floatValue();
+    }
+
+
+    /**
+     * Loads the DoubleValue from the variable with the given index.
+     */
+    public DoubleValue dload(int index)
+    {
+        return load(index).doubleValue();
+    }
+
+
+    /**
+     * Loads the ReferenceValue from the variable with the given index.
+     */
+    public ReferenceValue aload(int index)
+    {
+        return load(index).referenceValue();
+    }
+
+
+    /**
+     * Loads the InstructionOffsetValue from the variable with the given index.
+     */
+    public InstructionOffsetValue oload(int index)
+    {
+        return load(index).instructionOffsetValue();
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (this.getClass() != object.getClass())
+        {
+            return false;
+        }
+
+        Variables other = (Variables)object;
+
+        if (this.size != other.size)
+        {
+            return false;
+        }
+
+        for (int index = 0; index < size; index++)
+        {
+            Value thisValue  = this.values[index];
+            Value otherValue = other.values[index];
+
+            // Occasionally, two values of different types might be
+            // present in the same variable in a variable frame
+            // (corresponding to two local variables that share the
+            // same index), at some point outside of their scopes.
+            // We'll ignore these.
+            if (thisValue  != null &&
+                otherValue != null &&
+                thisValue.computationalType() == otherValue.computationalType() &&
+                !thisValue.equals(otherValue))
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+    public int hashCode()
+    {
+        int hashCode = size;
+
+        for (int index = 0; index < size; index++)
+        {
+            Value value = values[index];
+            if (value != null)
+            {
+                hashCode ^= value.hashCode();
+            }
+        }
+
+        return hashCode;
+    }
+
+
+    public String toString()
+    {
+        StringBuffer buffer = new StringBuffer();
+
+        for (int index = 0; index < size; index++)
+        {
+            Value value = values[index];
+            buffer = buffer.append('[')
+                           .append(value == null ? "empty" : value.toString())
+                           .append(']');
+        }
+
+        return buffer.toString();
+    }
+}
diff --git a/src/proguard/optimize/evaluation/package.html b/src/proguard/optimize/evaluation/package.html
new file mode 100644
index 0000000..5341f9f
--- /dev/null
+++ b/src/proguard/optimize/evaluation/package.html
@@ -0,0 +1,4 @@
+<body>
+This package contains visitors that perform partial evaluation and subsequent
+optimizations on byte code.
+</body>
diff --git a/src/proguard/optimize/evaluation/value/Category1Value.java b/src/proguard/optimize/evaluation/value/Category1Value.java
new file mode 100644
index 0000000..da9d845
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/Category1Value.java
@@ -0,0 +1,41 @@
+/* $Id: Category1Value.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+/**
+ * This abstract class represents a partially evaluated Category 1 value.
+ *
+ * @author Eric Lafortune
+ */
+abstract class Category1Value extends Value
+{
+    // Implementations for Value.
+
+    public final Category1Value category1Value()
+    {
+        return this;
+    }
+
+    public final boolean isCategory2()
+    {
+        return false;
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/Category2Value.java b/src/proguard/optimize/evaluation/value/Category2Value.java
new file mode 100644
index 0000000..eccdef9
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/Category2Value.java
@@ -0,0 +1,41 @@
+/* $Id: Category2Value.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+/**
+ * This abstract class represents a partially evaluated Category 2 value.
+ *
+ * @author Eric Lafortune
+ */
+abstract class Category2Value extends Value
+{
+    // Implementations for Value.
+
+    public final Category2Value category2Value()
+    {
+        return this;
+    }
+
+    public final boolean isCategory2()
+    {
+        return true;
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/DoubleValue.java b/src/proguard/optimize/evaluation/value/DoubleValue.java
new file mode 100644
index 0000000..ead4ef8
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/DoubleValue.java
@@ -0,0 +1,312 @@
+/* $Id: DoubleValue.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+/**
+ * This class represents a partially evaluated double value.
+ *
+ * @author Eric Lafortune
+ */
+public class DoubleValue extends Category2Value
+{
+    /**
+     * Returns the specific double value, if applicable.
+     */
+    public double value()
+    {
+        return 0.0;
+    }
+
+
+    // Basic binary methods.
+
+    /**
+     * Returns the generalization of this DoubleValue and the given other
+     * DoubleValue.
+     */
+    public DoubleValue generalize(DoubleValue other)
+    {
+        return this;
+    }
+
+
+    /**
+     * Returns the sum of this DoubleValue and the given DoubleValue.
+     */
+    public DoubleValue add(DoubleValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the difference of this DoubleValue and the given DoubleValue.
+     */
+    public DoubleValue subtract(DoubleValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the difference of the given DoubleValue and this DoubleValue.
+     */
+    public DoubleValue subtractFrom(DoubleValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the product of this DoubleValue and the given DoubleValue.
+     */
+    public DoubleValue multiply(DoubleValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the quotient of this DoubleValue and the given DoubleValue.
+     */
+    public DoubleValue divide(DoubleValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the quotient of the given DoubleValue and this DoubleValue.
+     */
+    public DoubleValue divideOf(DoubleValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the remainder of this DoubleValue divided by the given DoubleValue.
+     */
+    public DoubleValue remainder(DoubleValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the remainder of the given DoubleValue divided by this DoubleValue.
+     */
+    public DoubleValue remainderOf(DoubleValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns an IntegerValue with value -1, 0, or 1, if this DoubleValue is
+     * less than, equal to, or greater than the given DoubleValue, respectively.
+     */
+    public IntegerValue compare(DoubleValue other)
+    {
+        return IntegerValueFactory.create();
+    }
+
+
+    // Derived binary methods.
+
+    /**
+     * Returns an IntegerValue with value 1, 0, or -1, if this DoubleValue is
+     * less than, equal to, or greater than the given DoubleValue, respectively.
+     */
+    public final IntegerValue compareReverse(DoubleValue other)
+    {
+        return compare(other).negate();
+    }
+
+
+    // Basic unary methods.
+
+    /**
+     * Returns the negated value of this DoubleValue.
+     */
+    public DoubleValue negate()
+    {
+        return this;
+    }
+
+    /**
+     * Converts this DoubleValue to an IntegerValue.
+     */
+    public IntegerValue convertToInteger()
+    {
+        return IntegerValueFactory.create();
+    }
+
+    /**
+     * Converts this DoubleValue to a LongValue.
+     */
+    public LongValue convertToLong()
+    {
+        return LongValueFactory.create();
+    }
+
+    /**
+     * Converts this DoubleValue to a FloatValue.
+     */
+    public FloatValue convertToFloat()
+    {
+        return FloatValueFactory.create();
+    }
+
+
+    // Similar binary methods, but this time with more specific arguments.
+
+    /**
+     * Returns the generalization of this DoubleValue and the given other
+     * SpecificDoubleValue.
+     */
+    public DoubleValue generalize(SpecificDoubleValue other)
+    {
+        return this;
+    }
+
+
+    /**
+     * Returns the sum of this DoubleValue and the given SpecificDoubleValue.
+     */
+    public DoubleValue add(SpecificDoubleValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the difference of this DoubleValue and the given SpecificDoubleValue.
+     */
+    public DoubleValue subtract(SpecificDoubleValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the difference of the given SpecificDoubleValue and this DoubleValue.
+     */
+    public DoubleValue subtractFrom(SpecificDoubleValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the product of this DoubleValue and the given SpecificDoubleValue.
+     */
+    public DoubleValue multiply(SpecificDoubleValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the quotient of this DoubleValue and the given SpecificDoubleValue.
+     */
+    public DoubleValue divide(SpecificDoubleValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the quotient of the given SpecificDoubleValue and this
+     * DoubleValue.
+     */
+    public DoubleValue divideOf(SpecificDoubleValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the remainder of this DoubleValue divided by the given
+     * SpecificDoubleValue.
+     */
+    public DoubleValue remainder(SpecificDoubleValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the remainder of the given SpecificDoubleValue and this
+     * DoubleValue.
+     */
+    public DoubleValue remainderOf(SpecificDoubleValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns an IntegerValue with value -1, 0, or  1, if this DoubleValue is
+     * less than, equal to, or greater than the given SpecificDoubleValue,
+     * respectively.
+     */
+    public IntegerValue compare(SpecificDoubleValue other)
+    {
+        return IntegerValueFactory.create();
+    }
+
+
+    // Derived binary methods.
+
+    /**
+     * Returns an IntegerValue with value 1, 0, or -1, if this DoubleValue is
+     * less than, equal to, or greater than the given SpecificDoubleValue,
+     * respectively.
+     */
+    public final IntegerValue compareReverse(SpecificDoubleValue other)
+    {
+        return compare(other).negate();
+    }
+
+
+    // Implementations for Value.
+
+    public final DoubleValue doubleValue()
+    {
+        return this;
+    }
+
+    public final Value generalize(Value other)
+    {
+        return this.generalize(other.doubleValue());
+    }
+
+    public final int computationalType()
+    {
+        return TYPE_DOUBLE;
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        return object != null &&
+               this.getClass() == object.getClass();
+    }
+
+
+    public int hashCode()
+    {
+        return this.getClass().hashCode();
+    }
+
+
+    public String toString()
+    {
+        return "d";
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/DoubleValueFactory.java b/src/proguard/optimize/evaluation/value/DoubleValueFactory.java
new file mode 100644
index 0000000..0f5862c
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/DoubleValueFactory.java
@@ -0,0 +1,53 @@
+/* $Id: DoubleValueFactory.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+/**
+ * This class provides methods to create and reuse DoubleValue objects.
+ *
+ * @author Eric Lafortune
+ */
+public class DoubleValueFactory
+{
+    // Shared copies of DoubleValue objects, to avoid creating a lot of objects.
+    private static DoubleValue         DOUBLE_VALUE   = new DoubleValue();
+    private static SpecificDoubleValue DOUBLE_VALUE_0 = new SpecificDoubleValue(0.0);
+    private static SpecificDoubleValue DOUBLE_VALUE_1 = new SpecificDoubleValue(1.0);
+
+
+    /**
+     * Creates a new DoubleValue with an undefined value.
+     */
+    public static DoubleValue create()
+    {
+        return DOUBLE_VALUE;
+    }
+
+    /**
+     * Creates a new DoubleValue with a given specific value.
+     */
+    public static SpecificDoubleValue create(double value)
+    {
+        return value == 0.0 ? DOUBLE_VALUE_0 :
+               value == 1.0 ? DOUBLE_VALUE_1 :
+                              new SpecificDoubleValue(value);
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/FloatValue.java b/src/proguard/optimize/evaluation/value/FloatValue.java
new file mode 100644
index 0000000..8d5b237
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/FloatValue.java
@@ -0,0 +1,312 @@
+/* $Id: FloatValue.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+/**
+ * This class represents a partially evaluated float value.
+ *
+ * @author Eric Lafortune
+ */
+public class FloatValue extends Category1Value
+{
+    /**
+     * Returns the specific float value, if applicable.
+     */
+    public float value()
+    {
+        return 0f;
+    }
+
+
+    // Basic binary methods.
+
+    /**
+     * Returns the generalization of this FloatValue and the given other
+     * FloatValue.
+     */
+    public FloatValue generalize(FloatValue other)
+    {
+        return this;
+    }
+
+
+    /**
+     * Returns the sum of this FloatValue and the given FloatValue.
+     */
+    public FloatValue add(FloatValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the difference of this FloatValue and the given FloatValue.
+     */
+    public FloatValue subtract(FloatValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the difference of the given FloatValue and this FloatValue.
+     */
+    public FloatValue subtractFrom(FloatValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the product of this FloatValue and the given FloatValue.
+     */
+    public FloatValue multiply(FloatValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the quotient of this FloatValue and the given FloatValue.
+     */
+    public FloatValue divide(FloatValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the quotient of the given FloatValue and this FloatValue.
+     */
+    public FloatValue divideOf(FloatValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the remainder of this FloatValue divided by the given FloatValue.
+     */
+    public FloatValue remainder(FloatValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the remainder of the given FloatValue divided by this FloatValue.
+     */
+    public FloatValue remainderOf(FloatValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns an IntegerValue with value -1, 0, or 1, if this FloatValue is
+     * less than, equal to, or greater than the given FloatValue, respectively.
+     */
+    public IntegerValue compare(FloatValue other)
+    {
+        return IntegerValueFactory.create();
+    }
+
+
+    // Derived binary methods.
+
+    /**
+     * Returns an IntegerValue with value 1, 0, or -1, if this FloatValue is
+     * less than, equal to, or greater than the given FloatValue, respectively.
+     */
+    public final IntegerValue compareReverse(FloatValue other)
+    {
+        return compare(other).negate();
+    }
+
+
+    // Basic unary methods.
+
+    /**
+     * Returns the negated value of this FloatValue.
+     */
+    public FloatValue negate()
+    {
+        return this;
+    }
+
+    /**
+     * Converts this FloatValue to an IntegerValue.
+     */
+    public IntegerValue convertToInteger()
+    {
+        return IntegerValueFactory.create();
+    }
+
+    /**
+     * Converts this FloatValue to a LongValue.
+     */
+    public LongValue convertToLong()
+    {
+        return LongValueFactory.create();
+    }
+
+    /**
+     * Converts this FloatValue to a DoubleValue.
+     */
+    public DoubleValue convertToDouble()
+    {
+        return DoubleValueFactory.create();
+    }
+
+
+    // Similar binary methods, but this time with more specific arguments.
+
+    /**
+     * Returns the generalization of this FloatValue and the given other
+     * SpecificFloatValue.
+     */
+    public FloatValue generalize(SpecificFloatValue other)
+    {
+        return this;
+    }
+
+
+    /**
+     * Returns the sum of this FloatValue and the given SpecificFloatValue.
+     */
+    public FloatValue add(SpecificFloatValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the difference of this FloatValue and the given SpecificFloatValue.
+     */
+    public FloatValue subtract(SpecificFloatValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the difference of the given SpecificFloatValue and this FloatValue.
+     */
+    public FloatValue subtractFrom(SpecificFloatValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the product of this FloatValue and the given SpecificFloatValue.
+     */
+    public FloatValue multiply(SpecificFloatValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the quotient of this FloatValue and the given SpecificFloatValue.
+     */
+    public FloatValue divide(SpecificFloatValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the quotient of the given SpecificFloatValue and this
+     * FloatValue.
+     */
+    public FloatValue divideOf(SpecificFloatValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the remainder of this FloatValue divided by the given
+     * SpecificFloatValue.
+     */
+    public FloatValue remainder(SpecificFloatValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the remainder of the given SpecificFloatValue and this
+     * FloatValue.
+     */
+    public FloatValue remainderOf(SpecificFloatValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns an IntegerValue with value -1, 0, or  1, if this FloatValue is
+     * less than, equal to, or greater than the given SpecificFloatValue,
+     * respectively.
+     */
+    public IntegerValue compare(SpecificFloatValue other)
+    {
+        return IntegerValueFactory.create();
+    }
+
+
+    // Derived binary methods.
+
+    /**
+     * Returns an IntegerValue with value 1, 0, or -1, if this FloatValue is
+     * less than, equal to, or greater than the given SpecificFloatValue,
+     * respectively.
+     */
+    public final IntegerValue compareReverse(SpecificFloatValue other)
+    {
+        return compare(other).negate();
+    }
+
+
+    // Implementations for Value.
+
+    public final FloatValue floatValue()
+    {
+        return this;
+    }
+
+    public final Value generalize(Value other)
+    {
+        return this.generalize(other.floatValue());
+    }
+
+    public final int computationalType()
+    {
+        return TYPE_FLOAT;
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        return object != null &&
+               this.getClass() == object.getClass();
+    }
+
+
+    public int hashCode()
+    {
+        return this.getClass().hashCode();
+    }
+
+
+    public String toString()
+    {
+        return "f";
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/FloatValueFactory.java b/src/proguard/optimize/evaluation/value/FloatValueFactory.java
new file mode 100644
index 0000000..009989a
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/FloatValueFactory.java
@@ -0,0 +1,55 @@
+/* $Id: FloatValueFactory.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+/**
+ * This class provides methods to create and reuse FloatValue objects.
+ *
+ * @author Eric Lafortune
+ */
+public class FloatValueFactory
+{
+    // Shared copies of FloatValue objects, to avoid creating a lot of objects.
+    private static FloatValue         FLOAT_VALUE   = new FloatValue();
+    private static SpecificFloatValue FLOAT_VALUE_0 = new SpecificFloatValue(0.0f);
+    private static SpecificFloatValue FLOAT_VALUE_1 = new SpecificFloatValue(1.0f);
+    private static SpecificFloatValue FLOAT_VALUE_2 = new SpecificFloatValue(2.0f);
+
+
+    /**
+     * Creates a new FloatValue with an undefined value.
+     */
+    public static FloatValue create()
+    {
+        return FLOAT_VALUE;
+    }
+
+    /**
+     * Creates a new FloatValue with a given specific value.
+     */
+    public static SpecificFloatValue create(float value)
+    {
+        return value == 0.0f ? FLOAT_VALUE_0 :
+               value == 1.0f ? FLOAT_VALUE_1 :
+               value == 2.0f ? FLOAT_VALUE_2 :
+                               new SpecificFloatValue(value);
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/InstructionOffsetValue.java b/src/proguard/optimize/evaluation/value/InstructionOffsetValue.java
new file mode 100644
index 0000000..1f4c20b
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/InstructionOffsetValue.java
@@ -0,0 +1,237 @@
+/* $Id: InstructionOffsetValue.java,v 1.6 2004/12/04 19:34:42 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+/**
+ * This class represents a partially evaluated instruction offset. It can
+ * contain 0 or more specific instruction offsets.
+ *
+ * @author Eric Lafortune
+ */
+public class InstructionOffsetValue extends Category1Value
+{
+    private int[] values;
+
+
+    public InstructionOffsetValue()
+    {
+    }
+
+
+    public InstructionOffsetValue(int value)
+    {
+        this.values = new int[] { value };
+    }
+
+
+    public InstructionOffsetValue(int[] values)
+    {
+        this.values = values;
+    }
+
+
+    public int instructionOffsetCount()
+    {
+        return values == null ? 0 : values.length;
+    }
+
+
+    public int instructionOffset(int index)
+    {
+        return values[index];
+    }
+
+
+    /**
+     * Returns whether the given value is present in this list of instruction
+     * offsets.
+     */
+    public boolean contains(int value)
+    {
+        if (values != null)
+        {
+            for (int index = 0; index < values.length; index++)
+            {
+                if (values[index] == value)
+                {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Returns the generalization of this InstructionOffsetValue and the given
+     * other InstructionOffsetValue. The values of the other InstructionOffsetValue
+     * are guaranteed to remain at the end of the list, in the same order.
+     */
+    public final Value generalize(InstructionOffsetValue other)
+    {
+        // If the values array of either is null, return the other one.
+        if (this.values == null)
+        {
+            return other;
+        }
+
+        if (other.values == null)
+        {
+            return this;
+        }
+
+        // Compute the length of the union of the arrays.
+        int newLength = this.values.length;
+        for (int index = 0; index < other.values.length; index++)
+        {
+            if (!this.contains(other.values[index]))
+            {
+                newLength++;
+            }
+        }
+
+        // If the length of the union array is equal to the length of the values
+        // array of either, return it.
+        if (newLength == other.values.length)
+        {
+            return other;
+        }
+
+        // The ordering of the this array may not be right, so we can't just
+        // use it.
+        //if (newLength == this.values.length)
+        //{
+        //    return this;
+        //}
+
+        // Create the union array.
+        int[] newValues = new int[newLength];
+
+        int newIndex = 0;
+
+        // Copy the values that are different from the other array.
+        for (int index = 0; index < this.values.length; index++)
+        {
+            if (!other.contains(this.values[index]))
+            {
+                newValues[newIndex++] = this.values[index];
+            }
+        }
+
+        // Copy the values from the other array.
+        for (int index = 0; index < other.values.length; index++)
+        {
+            newValues[newIndex++] = other.values[index];
+        }
+
+        return InstructionOffsetValueFactory.create(newValues);
+    }
+
+
+    // Implementations for Value.
+
+    public final InstructionOffsetValue instructionOffsetValue()
+    {
+        return this;
+    }
+
+    public final Value generalize(Value other)
+    {
+        return this.generalize(other.instructionOffsetValue());
+    }
+
+    public final int computationalType()
+    {
+        return TYPE_INSTRUCTION_OFFSET;
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (object == null ||
+            this.getClass() != object.getClass())
+        {
+            return false;
+        }
+
+        InstructionOffsetValue other = (InstructionOffsetValue)object;
+        if (this.values == other.values)
+        {
+            return true;
+        }
+
+        if (this.values  == null ||
+            other.values == null ||
+            this.values.length != other.values.length)
+        {
+            return false;
+        }
+
+        for (int index = 0; index < other.values.length; index++)
+        {
+            if (!this.contains(other.values[index]))
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+    public int hashCode()
+    {
+        int hashCode = this.getClass().hashCode();
+
+        if (values != null)
+        {
+            for (int index = 0; index < values.length; index++)
+            {
+                hashCode ^= values[index];
+            }
+        }
+
+        return hashCode;
+    }
+
+
+    public String toString()
+    {
+        StringBuffer buffer = new StringBuffer("o:");
+
+        if (values != null)
+        {
+            for (int index = 0; index < values.length; index++)
+            {
+                if (index > 0)
+                {
+                    buffer.append(',');
+                }
+                buffer.append(values[index]);
+            }
+        }
+
+        return buffer.toString();
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/InstructionOffsetValueFactory.java b/src/proguard/optimize/evaluation/value/InstructionOffsetValueFactory.java
new file mode 100644
index 0000000..2c68cbe
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/InstructionOffsetValueFactory.java
@@ -0,0 +1,59 @@
+/* $Id: InstructionOffsetValueFactory.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+/**
+ * This class provides methods to create and reuse InstructionOffsetValue objects.
+ *
+ * @author Eric Lafortune
+ */
+public class InstructionOffsetValueFactory
+{
+    // Shared copies of InstructionOffsetValue objects, to avoid creating a lot of objects.
+    private static InstructionOffsetValue INSTRUCTION_OFFSET_VALUE = new InstructionOffsetValue();
+
+
+    /**
+     * Creates a new InstructionOffsetValue without a value.
+     */
+    public static InstructionOffsetValue create()
+    {
+        return INSTRUCTION_OFFSET_VALUE;
+    }
+
+
+    /**
+     * Creates a new InstructionOffsetValue with a given specific value.
+     */
+    public static InstructionOffsetValue create(int value)
+    {
+        return new InstructionOffsetValue(value);
+    }
+
+
+    /**
+     * Creates a new InstructionOffsetValue with a given list of possible values.
+     */
+    public static InstructionOffsetValue create(int[] values)
+    {
+        return new InstructionOffsetValue(values);
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/IntegerValue.java b/src/proguard/optimize/evaluation/value/IntegerValue.java
new file mode 100644
index 0000000..53d245a
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/IntegerValue.java
@@ -0,0 +1,622 @@
+/* $Id: IntegerValue.java,v 1.3 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+/**
+ * This class represents a partially evaluated integer value.
+ *
+ * @author Eric Lafortune
+ */
+public class IntegerValue extends Category1Value
+{
+    /**
+     * Returns the specific integer value, if applicable.
+     */
+    public int value()
+    {
+        return 0;
+    }
+
+
+    // Basic binary methods.
+
+    /**
+     * Returns the generalization of this IntegerValue and the given other
+     * IntegerValue.
+     */
+    public IntegerValue generalize(IntegerValue other)
+    {
+        return this;
+    }
+
+
+    /**
+     * Returns the sum of this IntegerValue and the given IntegerValue.
+     */
+    public IntegerValue add(IntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the difference of this IntegerValue and the given IntegerValue.
+     */
+    public IntegerValue subtract(IntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the difference of the given IntegerValue and this IntegerValue.
+     */
+    public IntegerValue subtractFrom(IntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the product of this IntegerValue and the given IntegerValue.
+     */
+    public IntegerValue multiply(IntegerValue other)
+    throws ArithmeticException
+    {
+        return this;
+    }
+
+    /**
+     * Returns the quotient of this IntegerValue and the given IntegerValue.
+     */
+    public IntegerValue divide(IntegerValue other)
+    throws ArithmeticException
+    {
+        return this;
+    }
+
+    /**
+     * Returns the quotient of the given IntegerValue and this IntegerValue.
+     */
+    public IntegerValue divideOf(IntegerValue other)
+    throws ArithmeticException
+    {
+        return this;
+    }
+
+    /**
+     * Returns the remainder of this IntegerValue divided by the given
+     * IntegerValue.
+     */
+    public IntegerValue remainder(IntegerValue other)
+    throws ArithmeticException
+    {
+        return this;
+    }
+
+    /**
+     * Returns the remainder of the given IntegerValue divided by this
+     * IntegerValue.
+     */
+    public IntegerValue remainderOf(IntegerValue other)
+    throws ArithmeticException
+    {
+        return this;
+    }
+
+    /**
+     * Returns this IntegerValue, shifted left by the given IntegerValue.
+     */
+    public IntegerValue shiftLeft(IntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the given IntegerValue, shifted left by this IntegerValue.
+     */
+    public IntegerValue shiftLeftOf(IntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns this IntegerValue, shifted right by the given IntegerValue.
+     */
+    public IntegerValue shiftRight(IntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the given IntegerValue, shifted right by this IntegerValue.
+     */
+    public IntegerValue shiftRightOf(IntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns this unsigned IntegerValue, shifted left by the given
+     * IntegerValue.
+     */
+    public IntegerValue unsignedShiftRight(IntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the given unsigned IntegerValue, shifted left by this
+     * IntegerValue.
+     */
+    public IntegerValue unsignedShiftRightOf(IntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the given LongValue, shifted left by this IntegerValue.
+     */
+    public LongValue shiftLeftOf(LongValue other)
+    {
+        return LongValueFactory.create();
+    }
+
+    /**
+     * Returns the given LongValue, shifted right by this IntegerValue.
+     */
+    public LongValue shiftRightOf(LongValue other)
+    {
+        return LongValueFactory.create();
+    }
+
+    /**
+     * Returns the given unsigned LongValue, shifted right by this IntegerValue.
+     */
+    public LongValue unsignedShiftRightOf(LongValue other)
+    {
+        return LongValueFactory.create();
+    }
+
+    /**
+     * Returns the logical <i>and</i> of this IntegerValue and the given
+     * IntegerValue.
+     */
+    public IntegerValue and(IntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the logical <i>or</i> of this IntegerValue and the given
+     * IntegerValue.
+     */
+    public IntegerValue or(IntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the logical <i>xor</i> of this IntegerValue and the given
+     * IntegerValue.
+     */
+    public IntegerValue xor(IntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns whether this IntegerValue and the given IntegerValue are equal:
+     * <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+     */
+    public int equal(IntegerValue other)
+    {
+        return MAYBE;
+    }
+
+    /**
+     * Returns whether this IntegerValue is less than the given IntegerValue:
+     * <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+     */
+    public int lessThan(IntegerValue other)
+    {
+        return MAYBE;
+    }
+
+    /**
+     * Returns whether this IntegerValue is less than or equal to the given
+     * IntegerValue: <code>NEVER</code>, <code>MAYBE</code>, or
+     * <code>ALWAYS</code>.
+     */
+    public int lessThanOrEqual(IntegerValue other)
+    {
+        return MAYBE;
+    }
+
+
+    // Derived binary methods.
+
+    /**
+     * Returns whether this IntegerValue and the given IntegerValue are different:
+     * <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+     */
+    public final int notEqual(IntegerValue other)
+    {
+        return -equal(other);
+    }
+
+    /**
+     * Returns whether this IntegerValue is greater than the given IntegerValue:
+     * <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+     */
+    public final int greaterThan(IntegerValue other)
+    {
+        return -lessThanOrEqual(other);
+    }
+
+    /**
+     * Returns whether this IntegerValue is greater than or equal to the given IntegerValue:
+     * <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+     */
+    public final int greaterThanOrEqual(IntegerValue other)
+    {
+        return -lessThan(other);
+    }
+
+
+    // Basic unary methods.
+
+    /**
+     * Returns the negated value of this IntegerValue.
+     */
+    public IntegerValue negate()
+    {
+        return this;
+    }
+
+    /**
+     * Converts this IntegerValue to a byte IntegerValue.
+     */
+    public IntegerValue convertToByte()
+    {
+        return this;
+    }
+
+    /**
+     * Converts this IntegerValue to a character IntegerValue.
+     */
+    public IntegerValue convertToCharacter()
+    {
+        return this;
+    }
+
+    /**
+     * Converts this IntegerValue to a short IntegerValue.
+     */
+    public IntegerValue convertToShort()
+    {
+        return this;
+    }
+
+    /**
+     * Converts this IntegerValue to a LongValue.
+     */
+    public LongValue convertToLong()
+    {
+        return LongValueFactory.create();
+    }
+
+    /**
+     * Converts this IntegerValue to a FloatValue.
+     */
+    public FloatValue convertToFloat()
+    {
+        return FloatValueFactory.create();
+    }
+
+    /**
+     * Converts this IntegerValue to a DoubleValue.
+     */
+    public DoubleValue convertToDouble()
+    {
+        return DoubleValueFactory.create();
+    }
+
+
+    // Similar binary methods, but this time with more specific arguments.
+
+    /**
+     * Returns the generalization of this IntegerValue and the given other
+     * SpecificIntegerValue.
+     */
+    public IntegerValue generalize(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+
+    /**
+     * Returns the sum of this IntegerValue and the given SpecificIntegerValue.
+     */
+    public IntegerValue add(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the difference of this IntegerValue and the given SpecificIntegerValue.
+     */
+    public IntegerValue subtract(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the difference of the given SpecificIntegerValue and this IntegerValue.
+     */
+    public IntegerValue subtractFrom(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the product of this IntegerValue and the given SpecificIntegerValue.
+     */
+    public IntegerValue multiply(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the quotient of this IntegerValue and the given
+     * SpecificIntegerValue.
+     */
+    public IntegerValue divide(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the quotient of the given SpecificIntegerValue and this
+     * IntegerValue.
+     */
+    public IntegerValue divideOf(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the remainder of this IntegerValue divided by the given
+     * SpecificIntegerValue.
+     */
+    public IntegerValue remainder(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the remainder of the given SpecificIntegerValue divided by this
+     * IntegerValue.
+     */
+    public IntegerValue remainderOf(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns this IntegerValue, shifted left by the given SpecificIntegerValue.
+     */
+    public IntegerValue shiftLeft(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the given SpecificIntegerValue, shifted left by this IntegerValue.
+     */
+    public IntegerValue shiftLeftOf(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns this IntegerValue, shifted right by the given SpecificIntegerValue.
+     */
+    public IntegerValue shiftRight(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the given SpecificIntegerValue, shifted right by this IntegerValue.
+     */
+    public IntegerValue shiftRightOf(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns this unsigned IntegerValue, shifted right by the given
+     * SpecificIntegerValue.
+     */
+    public IntegerValue unsignedShiftRight(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the given unsigned SpecificIntegerValue, shifted right by this
+     * IntegerValue.
+     */
+    public IntegerValue unsignedShiftRightOf(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the given SpecificLongValue, shifted left by this IntegerValue.
+     */
+    public LongValue shiftLeftOf(SpecificLongValue other)
+    {
+        return LongValueFactory.create();
+    }
+
+    /**
+     * Returns the given SpecificLongValue, shifted right by this IntegerValue.
+     */
+    public LongValue shiftRightOf(SpecificLongValue other)
+    {
+        return LongValueFactory.create();
+    }
+
+    /**
+     * Returns the given unsigned SpecificLongValue, shifted right by this
+     * IntegerValue.
+     */
+    public LongValue unsignedShiftRightOf(SpecificLongValue other)
+    {
+        return LongValueFactory.create();
+    }
+
+    /**
+     * Returns the logical <i>and</i> of this IntegerValue and the given
+     * SpecificIntegerValue.
+     */
+    public IntegerValue and(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the logical <i>or</i> of this IntegerValue and the given
+     * SpecificIntegerValue.
+     */
+    public IntegerValue or(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the logical <i>xor</i> of this IntegerValue and the given
+     * SpecificIntegerValue.
+     */
+    public IntegerValue xor(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns whether this IntegerValue and the given SpecificIntegerValue are
+     * equal: <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+     */
+    public int equal(SpecificIntegerValue other)
+    {
+        return MAYBE;
+    }
+
+    /**
+     * Returns whether this IntegerValue is less than the given
+     * SpecificIntegerValue: <code>NEVER</code>, <code>MAYBE</code>, or
+     * <code>ALWAYS</code>.
+     */
+    public int lessThan(SpecificIntegerValue other)
+    {
+        return MAYBE;
+    }
+
+    /**
+     * Returns whether this IntegerValue is less than or equal to the given
+     * SpecificIntegerValue: <code>NEVER</code>, <code>MAYBE</code>, or
+     * <code>ALWAYS</code>.
+     */
+    public int lessThanOrEqual(SpecificIntegerValue other)
+    {
+        return MAYBE;
+    }
+
+
+    // Derived binary methods.
+
+    /**
+     * Returns whether this IntegerValue and the given SpecificIntegerValue are
+     * different: <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+     */
+    public final int notEqual(SpecificIntegerValue other)
+    {
+        return -equal(other);
+    }
+
+    /**
+     * Returns whether this IntegerValue is greater than the given
+     * SpecificIntegerValue: <code>NEVER</code>, <code>MAYBE</code>, or
+     * <code>ALWAYS</code>.
+     */
+    public final int greaterThan(SpecificIntegerValue other)
+    {
+        return -lessThanOrEqual(other);
+    }
+
+    /**
+     * Returns whether this IntegerValue is greater than or equal to the given
+     * SpecificIntegerValue: <code>NEVER</code>, <code>MAYBE</code>, or
+     * <code>ALWAYS</code>.
+     */
+    public final int greaterThanOrEqual(SpecificIntegerValue other)
+    {
+        return -lessThan(other);
+    }
+
+
+    // Implementations for Value.
+
+    public final IntegerValue integerValue()
+    {
+        return this;
+    }
+
+    public final Value generalize(Value other)
+    {
+        return this.generalize(other.integerValue());
+    }
+
+    public final int computationalType()
+    {
+        return TYPE_INTEGER;
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        return object != null &&
+               this.getClass() == object.getClass();
+    }
+
+
+    public int hashCode()
+    {
+        return this.getClass().hashCode();
+    }
+
+
+    public String toString()
+    {
+        return "i";
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/IntegerValueFactory.java b/src/proguard/optimize/evaluation/value/IntegerValueFactory.java
new file mode 100644
index 0000000..19c3e04
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/IntegerValueFactory.java
@@ -0,0 +1,66 @@
+/* $Id: IntegerValueFactory.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+/**
+ * This class provides methods to create and reuse IntegerValue objects.
+ *
+ * @author Eric Lafortune
+ */
+public class IntegerValueFactory
+{
+    // Shared copies of IntegerValue objects, to avoid creating a lot of objects.
+    private static IntegerValue         INTEGER_VALUE    = new IntegerValue();
+    private static SpecificIntegerValue INTEGER_VALUE_M1 = new SpecificIntegerValue(-1);
+    private static SpecificIntegerValue INTEGER_VALUE_0  = new SpecificIntegerValue(0);
+    private static SpecificIntegerValue INTEGER_VALUE_1  = new SpecificIntegerValue(1);
+    private static SpecificIntegerValue INTEGER_VALUE_2  = new SpecificIntegerValue(2);
+    private static SpecificIntegerValue INTEGER_VALUE_3  = new SpecificIntegerValue(3);
+    private static SpecificIntegerValue INTEGER_VALUE_4  = new SpecificIntegerValue(4);
+    private static SpecificIntegerValue INTEGER_VALUE_5  = new SpecificIntegerValue(5);
+
+    /**
+     * Creates a new IntegerValue with an undefined value.
+     */
+    public static IntegerValue create()
+    {
+        return INTEGER_VALUE;
+    }
+
+    /**
+     * Creates a new IntegerValue with a given specific value.
+     */
+    public static SpecificIntegerValue create(int value)
+    {
+        switch (value)
+        {
+            case -1: return INTEGER_VALUE_M1;
+            case  0: return INTEGER_VALUE_0;
+            case  1: return INTEGER_VALUE_1;
+            case  2: return INTEGER_VALUE_2;
+            case  3: return INTEGER_VALUE_3;
+            case  4: return INTEGER_VALUE_4;
+            case  5: return INTEGER_VALUE_5;
+            default: return new SpecificIntegerValue(value);
+        }
+    }
+
+}
diff --git a/src/proguard/optimize/evaluation/value/LongValue.java b/src/proguard/optimize/evaluation/value/LongValue.java
new file mode 100644
index 0000000..e625d38
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/LongValue.java
@@ -0,0 +1,390 @@
+/* $Id: LongValue.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+/**
+ * This class represents a partially evaluated long value.
+ *
+ * @author Eric Lafortune
+ */
+public class LongValue extends Category2Value
+{
+    /**
+     * Returns the specific long value, if applicable.
+     */
+    public long value()
+    {
+        return 0L;
+    }
+
+
+    // Basic binary methods.
+
+    /**
+     * Returns the generalization of this LongValue and the given other
+     * LongValue.
+     */
+    public LongValue generalize(LongValue other)
+    {
+        return this;
+    }
+
+
+    /**
+     * Returns the sum of this LongValue and the given LongValue.
+     */
+    public LongValue add(LongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the difference of this LongValue and the given LongValue.
+     */
+    public LongValue subtract(LongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the difference of the given LongValue and this LongValue.
+     */
+    public LongValue subtractFrom(LongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the product of this LongValue and the given LongValue.
+     */
+    public LongValue multiply(LongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the quotient of this LongValue and the given LongValue.
+     */
+    public LongValue divide(LongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the quotient of the given LongValue and this LongValue.
+     */
+    public LongValue divideOf(LongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the remainder of this LongValue divided by the given LongValue.
+     */
+    public LongValue remainder(LongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the remainder of the given LongValue divided by this LongValue.
+     */
+    public LongValue remainderOf(LongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns this LongValue, shifted left by the given IntegerValue.
+     */
+    public LongValue shiftLeft(IntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns this LongValue, shifted right by the given IntegerValue.
+     */
+    public LongValue shiftRight(IntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns this unsigned LongValue, shifted left by the given
+     * IntegerValue.
+     */
+    public LongValue unsignedShiftRight(IntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the logical <i>and</i> of this LongValue and the given
+     * LongValue.
+     */
+    public LongValue and(LongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the logical <i>or</i> of this LongValue and the given
+     * LongValue.
+     */
+    public LongValue or(LongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the logical <i>xor</i> of this LongValue and the given
+     * LongValue.
+     */
+    public LongValue xor(LongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns an IntegerValue with value -1, 0, or 1, if this LongValue is
+     * less than, equal to, or greater than the given LongValue, respectively.
+     */
+    public IntegerValue compare(LongValue other)
+    {
+        return IntegerValueFactory.create();
+    }
+
+
+    // Derived binary methods.
+
+    /**
+     * Returns an IntegerValue with value 1, 0, or -1, if this LongValue is
+     * less than, equal to, or greater than the given LongValue, respectively.
+     */
+    public final IntegerValue compareReverse(LongValue other)
+    {
+        return compare(other).negate();
+    }
+
+
+    // Basic unary methods.
+
+    /**
+     * Returns the negated value of this LongValue.
+     */
+    public LongValue negate()
+    {
+        return this;
+    }
+
+    /**
+     * Converts this LongValue to an IntegerValue.
+     */
+    public IntegerValue convertToInteger()
+    {
+        return IntegerValueFactory.create();
+    }
+
+    /**
+     * Converts this LongValue to a FloatValue.
+     */
+    public FloatValue convertToFloat()
+    {
+        return FloatValueFactory.create();
+    }
+
+    /**
+     * Converts this LongValue to a DoubleValue.
+     */
+    public DoubleValue convertToDouble()
+    {
+        return DoubleValueFactory.create();
+    }
+
+
+    // Similar binary methods, but this time with more specific arguments.
+
+    /**
+     * Returns the generalization of this LongValue and the given other
+     * SpecificLongValue.
+     */
+    public LongValue generalize(SpecificLongValue other)
+    {
+        return this;
+    }
+
+
+    /**
+     * Returns the sum of this LongValue and the given SpecificLongValue.
+     */
+    public LongValue add(SpecificLongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the difference of this LongValue and the given SpecificLongValue.
+     */
+    public LongValue subtract(SpecificLongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the difference of the given SpecificLongValue and this LongValue.
+     */
+    public LongValue subtractFrom(SpecificLongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the product of this LongValue and the given SpecificLongValue.
+     */
+    public LongValue multiply(SpecificLongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the quotient of this LongValue and the given SpecificLongValue.
+     */
+    public LongValue divide(SpecificLongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the quotient of the given SpecificLongValue and this LongValue.
+     */
+    public LongValue divideOf(SpecificLongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the remainder of this LongValue divided by the given
+     * SpecificLongValue.
+     */
+    public LongValue remainder(SpecificLongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the remainder of the given SpecificLongValue and this
+     * LongValue.
+     */
+    public LongValue remainderOf(SpecificLongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the logical <i>and</i> of this LongValue and the given
+     * SpecificLongValue.
+     */
+    public LongValue and(SpecificLongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the logical <i>or</i> of this LongValue and the given
+     * SpecificLongValue.
+     */
+    public LongValue or(SpecificLongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns the logical <i>xor</i> of this LongValue and the given
+     * SpecificLongValue.
+     */
+    public LongValue xor(SpecificLongValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns an IntegerValue with value -1, 0, or 1, if this LongValue is
+     * less than, equal to, or greater than the given SpecificLongValue,
+     * respectively.
+     */
+    public IntegerValue compare(SpecificLongValue other)
+    {
+        return IntegerValueFactory.create();
+    }
+
+
+    // Derived binary methods.
+
+    /**
+     * Returns an IntegerValue with value 1, 0, or -1, if this LongValue is
+     * less than, equal to, or greater than the given SpecificLongValue,
+     * respectively.
+     */
+    public final IntegerValue compareReverse(SpecificLongValue other)
+    {
+        return compare(other).negate();
+    }
+
+
+    // Implementations for Value.
+
+    public final LongValue longValue()
+    {
+        return this;
+    }
+
+    public final Value generalize(Value other)
+    {
+        return this.generalize(other.longValue());
+    }
+
+    public final int computationalType()
+    {
+        return TYPE_LONG;
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        return object != null &&
+               this.getClass() == object.getClass();
+    }
+
+
+    public int hashCode()
+    {
+        return this.getClass().hashCode();
+    }
+
+
+    public String toString()
+    {
+        return "l";
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/LongValueFactory.java b/src/proguard/optimize/evaluation/value/LongValueFactory.java
new file mode 100644
index 0000000..38475da
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/LongValueFactory.java
@@ -0,0 +1,53 @@
+/* $Id: LongValueFactory.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+/**
+ * This class provides methods to create and reuse LongValue objects.
+ *
+ * @author Eric Lafortune
+ */
+public class LongValueFactory
+{
+    // Shared copies of LongValue objects, to avoid creating a lot of objects.
+    private static LongValue         LONG_VALUE   = new LongValue();
+    private static SpecificLongValue LONG_VALUE_0 = new SpecificLongValue(0);
+    private static SpecificLongValue LONG_VALUE_1 = new SpecificLongValue(1);
+
+
+    /**
+     * Creates a new LongValue with an undefined value.
+     */
+    public static LongValue create()
+    {
+        return LONG_VALUE;
+    }
+
+    /**
+     * Creates a new LongValue with a given specific value.
+     */
+    public static SpecificLongValue create(long value)
+    {
+        return value == 0 ? LONG_VALUE_0 :
+               value == 1 ? LONG_VALUE_1 :
+                            new SpecificLongValue(value);
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/ReferenceValue.java b/src/proguard/optimize/evaluation/value/ReferenceValue.java
new file mode 100644
index 0000000..2d15729
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/ReferenceValue.java
@@ -0,0 +1,190 @@
+/* $Id: ReferenceValue.java,v 1.3 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+import proguard.classfile.*;
+import proguard.classfile.ClassCpInfo;
+
+/**
+ * This class represents a partially evaluated reference value.
+ *
+ * @author Eric Lafortune
+ */
+public class ReferenceValue extends Category1Value
+{
+    protected boolean mayBeNull;
+
+
+    /**
+     * Creates a new reference value that may or may not be null.
+     */
+    public ReferenceValue(boolean mayBeNull)
+    {
+        this.mayBeNull = mayBeNull;
+    }
+
+
+    /**
+     * Returns the specific reference value, if applicable.
+     */
+    public ClassFile value()
+    {
+        return null;
+    }
+
+
+    /**
+     * Returns the array dimension, if applicable.
+     */
+    public int dimension()
+    {
+        return 0;
+    }
+
+
+    // Basic binary methods.
+
+    /**
+     * Returns the generalization of this ReferenceValue and the given other
+     * ReferenceValue.
+     */
+    public ReferenceValue generalize(ReferenceValue other)
+    {
+        return ReferenceValueFactory.create(this.mayBeNull || other.mayBeNull);
+    }
+
+
+    /**
+     * Returns whether this ReferenceValue and the given ReferenceValue are equal:
+     * <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+     */
+    public int equal(ReferenceValue other)
+    {
+        return MAYBE;
+    }
+
+
+    // Basic unary methods.
+
+    /**
+     * Returns whether this ReferenceValue is <code>null</code>:
+     * <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+     */
+    public int isNull()
+    {
+        return mayBeNull ? MAYBE : NEVER;
+    }
+
+
+    /**
+     * Returns whether this ReferenceValue is an instance of the given class
+     * with the given dimensionality:
+     * <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+     */
+    public int instanceOf(ClassFile typeClassFile, int typeDimension)
+    {
+        return MAYBE;
+    }
+
+
+    // Derived binary methods.
+
+    /**
+     * Returns whether this ReferenceValue and the given ReferenceValue are different:
+     * <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+     */
+    public final int notEqual(ReferenceValue other)
+    {
+        return -equal(other);
+    }
+
+
+    /**
+     * Returns whether this ReferenceValue is not <code>null</code>:
+     * <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+     */
+    public final int isNotNull()
+    {
+        return -isNull();
+    }
+
+
+    // Similar binary methods, but this time with more specific arguments.
+
+    /**
+     * Returns the generalization of this ReferenceValue and the given other
+     * SpecificReferenceValue.
+     */
+    public ReferenceValue generalize(SpecificReferenceValue other)
+    {
+        return ReferenceValueFactory.create(this.mayBeNull || other.mayBeNull);
+    }
+
+
+    /**
+     * Returns whether this ReferenceValue and the given SpecificReferenceValue are
+     * equal: <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+     */
+    public int equal(SpecificReferenceValue other)
+    {
+        return MAYBE;
+    }
+
+
+    // Implementations for Value.
+
+    public final ReferenceValue referenceValue()
+    {
+        return this;
+    }
+
+    public final Value generalize(Value other)
+    {
+        return this.generalize(other.referenceValue());
+    }
+
+    public final int computationalType()
+    {
+        return TYPE_REFERENCE;
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        return object != null &&
+               this.getClass() == object.getClass() &&
+               this.mayBeNull  == ((ReferenceValue)object).mayBeNull;
+    }
+
+
+    public int hashCode()
+    {
+        return this.getClass().hashCode() ^ (mayBeNull ? 0 : 1);
+    }
+
+
+    public String toString()
+    {
+        return mayBeNull ? "a" : "a:!null";
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/ReferenceValueFactory.java b/src/proguard/optimize/evaluation/value/ReferenceValueFactory.java
new file mode 100644
index 0000000..30d8da7
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/ReferenceValueFactory.java
@@ -0,0 +1,80 @@
+/* $Id: ReferenceValueFactory.java,v 1.3 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+import proguard.classfile.*;
+import proguard.classfile.ClassCpInfo;
+
+/**
+ * This class provides methods to create and reuse ReferenceValue objects.
+ *
+ * @author Eric Lafortune
+ */
+public class ReferenceValueFactory
+{
+    // Shared copies of ReferenceValue objects, to avoid creating a lot of objects.
+    private static ReferenceValue REFERENCE_VALUE_MAYBE_NULL = new ReferenceValue(true);
+    private static ReferenceValue REFERENCE_VALUE_NOT_NULL   = new ReferenceValue(false);
+
+
+    /**
+     * Creates a new ReferenceValue with an undefined value.
+     */
+    public static ReferenceValue create(boolean mayBeNull)
+    {
+        return mayBeNull ? REFERENCE_VALUE_MAYBE_NULL :
+                           REFERENCE_VALUE_NOT_NULL;
+    }
+
+
+    /**
+     * Creates a new ReferenceValue that represents <code>null</code>.
+     */
+    public static ReferenceValue createNull()
+    {
+        return new SpecificReferenceValue(null, true);
+    }
+
+
+    /**
+     * Creates a new ReferenceValue of a specific type. If the value is
+     * <code>null</code>, a ReferenceValue of an undefined type is returned.
+     */
+    public static ReferenceValue create(ClassFile value, boolean mayBeNull)
+    {
+        return value == null ? ReferenceValueFactory.create(mayBeNull) :
+                               new SpecificReferenceValue(value, mayBeNull);
+    }
+
+
+    /**
+     * Creates a new array ReferenceValue of a specific type and dimensionality.
+     * If the value is <code>null</code>, a ReferenceValue of an undefined
+     * type is returned. If the dimension is 0, a ReferenceValue of the given
+     * type is returned.
+     */
+    public static ReferenceValue create(ClassFile value, int dimension, boolean mayBeNull)
+    {
+        return value == null  ? ReferenceValueFactory.create(mayBeNull)        :
+               dimension == 0 ? ReferenceValueFactory.create(value, mayBeNull) :
+                                new SpecificArrayReferenceValue(value, dimension, mayBeNull);
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/SpecificArrayReferenceValue.java b/src/proguard/optimize/evaluation/value/SpecificArrayReferenceValue.java
new file mode 100644
index 0000000..0a4ec34
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/SpecificArrayReferenceValue.java
@@ -0,0 +1,160 @@
+/* $Id: SpecificArrayReferenceValue.java,v 1.4 2004/08/21 21:37:28 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+import proguard.classfile.*;
+import proguard.classfile.ClassCpInfo;
+import proguard.classfile.util.ClassUtil;
+
+/**
+ * This ReferenceValue represents an array reference value of a specific type
+ * and dimensionality.
+ *
+ * @author Eric Lafortune
+ */
+class SpecificArrayReferenceValue extends SpecificReferenceValue
+{
+    protected int dimension;
+
+
+    /**
+     * Creates a new specific array reference value. The dimensionality of the
+     * value argument alone is ignored.
+     */
+    public SpecificArrayReferenceValue(ClassFile value, int dimension, boolean mayBeNull)
+    {
+        super(value, mayBeNull);
+
+        this.dimension = dimension;
+    }
+
+
+    // Implementations for ReferenceValue.
+
+    public int dimension()
+    {
+        return dimension;
+    }
+
+
+    // Implementations of binary methods of ReferenceValue.
+
+    // Perhaps the other value arguments are more specific than apparent
+    // in these methods, so delegate to them.
+
+    public ReferenceValue generalize(ReferenceValue other)
+    {
+        return other.generalize(this);
+    }
+
+    public int equal(ReferenceValue other)
+    {
+        return other.equal(this);
+    }
+
+
+    // Implementations of unary methods of ReferenceValue.
+
+    public int instanceOf(ClassFile typeClassFile, int typeDimension)
+    {
+        // If this value is null, it is never an instance of any class.
+        if (value == null)
+        {
+            return NEVER;
+        }
+
+        // If the type class is unknown or the type has a higher dimension, we
+        // can't tell for sure.
+        if (typeClassFile == null ||
+            typeDimension > dimension)
+        {
+            return MAYBE;
+        }
+
+        // If the type dimension is less than the value's dimension, the type
+        // must be Object, Cloneable, or Serializable.
+        if (typeDimension < dimension)
+        {
+            String typeClassName = typeClassFile.getName();
+            return typeClassName.equals(ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT)    ||
+                   typeClassName.equals(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLONEABLE) ||
+                   typeClassName.equals(ClassConstants.INTERNAL_NAME_JAVA_IO_SERIALIZABLE) ?
+                       ALWAYS :
+                       NEVER;
+        }
+
+        // If the value extends the type, we're sure.
+        return value.extends_(typeClassFile) ||
+               value.implements_(typeClassFile) ?
+                   ALWAYS :
+                   MAYBE;
+    }
+
+
+    // Implementations of binary ReferenceValue methods with
+    // SpecificArrayReferenceValue arguments.
+
+    public ReferenceValue generalize(SpecificArrayReferenceValue other)
+    {
+        return this.value     == other.value &&
+               this.dimension == other.dimension ?
+            this.mayBeNull ? this : other :
+            ReferenceValueFactory.create(this.mayBeNull || other.mayBeNull);
+    }
+
+    public int equal(SpecificArrayReferenceValue other)
+    {
+        return this.value  == null &&
+               other.value == null ? ALWAYS : MAYBE;
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (object == null ||
+            this.getClass() != object.getClass())
+        {
+            return false;
+        }
+
+        SpecificArrayReferenceValue other = (SpecificArrayReferenceValue)object;
+        return this.mayBeNull == other.mayBeNull                     &&
+               (this.value == null ? other.value == null :
+                                     this.value.equals(other.value)) &&
+               this.dimension == other.dimension;
+    }
+
+
+    public int hashCode()
+    {
+        return this.getClass().hashCode()             ^
+               (value == null ? 0 : value.hashCode()) ^
+               dimension;
+    }
+
+
+    public String toString()
+    {
+        return "a:" + (value == null ? "null" : value.getName()+"["+dimension+"]");
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/SpecificDoubleValue.java b/src/proguard/optimize/evaluation/value/SpecificDoubleValue.java
new file mode 100644
index 0000000..f957ca6
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/SpecificDoubleValue.java
@@ -0,0 +1,213 @@
+/* $Id: SpecificDoubleValue.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License adouble
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+/**
+ * This DoubleValue represents a specific double value.
+ *
+ * @author Eric Lafortune
+ */
+class SpecificDoubleValue extends DoubleValue
+{
+    private double value;
+
+
+    /**
+     * Creates a new specific double value.
+     */
+    public SpecificDoubleValue(double value)
+    {
+        this.value = value;
+    }
+
+
+    // Implementations for DoubleValue.
+
+    public double value()
+    {
+        return value;
+    }
+
+
+    // Implementations of binary methods of DoubleValue.
+
+    // Perhaps the other value arguments are more specific than apparent
+    // in these methods, so delegate to them.
+
+    public DoubleValue generalize(DoubleValue other)
+    {
+        return other.generalize(this);
+    }
+
+    public DoubleValue add(DoubleValue other)
+    {
+        return other.add(this);
+    }
+
+    public DoubleValue subtract(DoubleValue other)
+    {
+        return other.subtractFrom(this);
+    }
+
+    public DoubleValue subtractFrom(DoubleValue other)
+    {
+        return other.subtract(this);
+    }
+
+    public DoubleValue multiply(DoubleValue other)
+    {
+        return other.multiply(this);
+    }
+
+    public DoubleValue divide(DoubleValue other)
+    {
+        return other.divideOf(this);
+    }
+
+    public DoubleValue divideOf(DoubleValue other)
+    {
+        return other.divide(this);
+    }
+
+    public DoubleValue remainder(DoubleValue other)
+    {
+        return other.remainderOf(this);
+    }
+
+    public DoubleValue remainderOf(DoubleValue other)
+    {
+        return other.remainder(this);
+    }
+
+    public IntegerValue compare(DoubleValue other)
+    {
+        return other.compareReverse(this);
+    }
+
+
+    // Implementations of unary methods of DoubleValue.
+
+    public DoubleValue negate()
+    {
+        return DoubleValueFactory.create(-value);
+    }
+
+    public IntegerValue convertToInteger()
+    {
+        return IntegerValueFactory.create((int)value);
+    }
+
+    public LongValue convertToLong()
+    {
+        return LongValueFactory.create((long)value);
+    }
+
+    public FloatValue convertToFloat()
+    {
+        return FloatValueFactory.create((float)value);
+    }
+
+
+    // Implementations of binary DoubleValue methods with SpecificDoubleValue
+    // arguments.
+
+    public DoubleValue generalize(SpecificDoubleValue other)
+    {
+        return this.value == other.value ? this : DoubleValueFactory.create();
+    }
+
+    public DoubleValue add(SpecificDoubleValue other)
+    {
+        return DoubleValueFactory.create(this.value + other.value);
+    }
+
+    public DoubleValue subtract(SpecificDoubleValue other)
+    {
+        return DoubleValueFactory.create(this.value - other.value);
+    }
+
+    public DoubleValue subtractFrom(SpecificDoubleValue other)
+    {
+        return DoubleValueFactory.create(other.value - this.value);
+    }
+
+    public DoubleValue multiply(SpecificDoubleValue other)
+    {
+        return DoubleValueFactory.create(this.value * other.value);
+    }
+
+    public DoubleValue divide(SpecificDoubleValue other)
+    {
+        return DoubleValueFactory.create(this.value / other.value);
+    }
+
+    public DoubleValue divideOf(SpecificDoubleValue other)
+    {
+        return DoubleValueFactory.create(other.value / this.value);
+    }
+
+    public DoubleValue remainder(SpecificDoubleValue other)
+    {
+        return DoubleValueFactory.create(this.value % other.value);
+    }
+
+    public DoubleValue remainderOf(SpecificDoubleValue other)
+    {
+        return DoubleValueFactory.create(other.value % this.value);
+    }
+
+    public IntegerValue compare(SpecificDoubleValue other)
+    {
+        return this.value <  other.value ? IntegerValueFactory.create(-1) :
+               this.value == other.value ? IntegerValueFactory.create(0) :
+                                           IntegerValueFactory.create(1);
+    }
+
+
+    // Implementations for Value.
+
+    public boolean isSpecific()
+    {
+        return true;
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        return object          != null              &&
+               this.getClass() == object.getClass() &&
+               this.value      == ((SpecificDoubleValue)object).value;
+    }
+
+
+    public int hashCode()
+    {
+        return this.getClass().hashCode() ^ (int)Double.doubleToLongBits(value);
+    }
+
+
+    public String toString()
+    {
+        return "d:"+value;
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/SpecificFloatValue.java b/src/proguard/optimize/evaluation/value/SpecificFloatValue.java
new file mode 100644
index 0000000..b8332a4
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/SpecificFloatValue.java
@@ -0,0 +1,213 @@
+/* $Id: SpecificFloatValue.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License afloat
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+/**
+ * This FloatValue represents a specific float value.
+ *
+ * @author Eric Lafortune
+ */
+class SpecificFloatValue extends FloatValue
+{
+    private float value;
+
+
+    /**
+     * Creates a new specific float value.
+     */
+    public SpecificFloatValue(float value)
+    {
+        this.value = value;
+    }
+
+
+    // Implementations for FloatValue.
+
+    public float value()
+    {
+        return value;
+    }
+
+
+    // Implementations of binary methods of FloatValue.
+
+    // Perhaps the other value arguments are more specific than apparent
+    // in these methods, so delegate to them.
+
+    public FloatValue generalize(FloatValue other)
+    {
+        return other.generalize(this);
+    }
+
+    public FloatValue add(FloatValue other)
+    {
+        return other.add(this);
+    }
+
+    public FloatValue subtract(FloatValue other)
+    {
+        return other.subtractFrom(this);
+    }
+
+    public FloatValue subtractFrom(FloatValue other)
+    {
+        return other.subtract(this);
+    }
+
+    public FloatValue multiply(FloatValue other)
+    {
+        return other.multiply(this);
+    }
+
+    public FloatValue divide(FloatValue other)
+    {
+        return other.divideOf(this);
+    }
+
+    public FloatValue divideOf(FloatValue other)
+    {
+        return other.divide(this);
+    }
+
+    public FloatValue remainder(FloatValue other)
+    {
+        return other.remainderOf(this);
+    }
+
+    public FloatValue remainderOf(FloatValue other)
+    {
+        return other.remainder(this);
+    }
+
+    public IntegerValue compare(FloatValue other)
+    {
+        return other.compareReverse(this);
+    }
+
+
+    // Implementations of unary methods of FloatValue.
+
+    public FloatValue negate()
+    {
+        return FloatValueFactory.create(-value);
+    }
+
+    public IntegerValue convertToInteger()
+    {
+        return IntegerValueFactory.create((int)value);
+    }
+
+    public LongValue convertToLong()
+    {
+        return LongValueFactory.create((long)value);
+    }
+
+    public DoubleValue convertToDouble()
+    {
+        return DoubleValueFactory.create((double)value);
+    }
+
+
+    // Implementations of binary FloatValue methods with SpecificFloatValue
+    // arguments.
+
+    public FloatValue generalize(SpecificFloatValue other)
+    {
+        return this.value == other.value ? this : FloatValueFactory.create();
+    }
+
+    public FloatValue add(SpecificFloatValue other)
+    {
+        return FloatValueFactory.create(this.value + other.value);
+    }
+
+    public FloatValue subtract(SpecificFloatValue other)
+    {
+        return FloatValueFactory.create(this.value - other.value);
+    }
+
+    public FloatValue subtractFrom(SpecificFloatValue other)
+    {
+        return FloatValueFactory.create(other.value - this.value);
+    }
+
+    public FloatValue multiply(SpecificFloatValue other)
+    {
+        return FloatValueFactory.create(this.value * other.value);
+    }
+
+    public FloatValue divide(SpecificFloatValue other)
+    {
+        return FloatValueFactory.create(this.value / other.value);
+    }
+
+    public FloatValue divideOf(SpecificFloatValue other)
+    {
+        return FloatValueFactory.create(other.value / this.value);
+    }
+
+    public FloatValue remainder(SpecificFloatValue other)
+    {
+        return FloatValueFactory.create(this.value % other.value);
+    }
+
+    public FloatValue remainderOf(SpecificFloatValue other)
+    {
+        return FloatValueFactory.create(other.value % this.value);
+    }
+
+    public IntegerValue compare(SpecificFloatValue other)
+    {
+        return this.value <  other.value ? IntegerValueFactory.create(-1) :
+               this.value == other.value ? IntegerValueFactory.create(0) :
+                                           IntegerValueFactory.create(1);
+    }
+
+
+    // Implementations for Value.
+
+    public boolean isSpecific()
+    {
+        return true;
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        return object          != null              &&
+               this.getClass() == object.getClass() &&
+               this.value      == ((SpecificFloatValue)object).value;
+    }
+
+
+    public int hashCode()
+    {
+        return this.getClass().hashCode() ^ Float.floatToIntBits(value);
+    }
+
+
+    public String toString()
+    {
+        return "f:"+value;
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/SpecificIntegerValue.java b/src/proguard/optimize/evaluation/value/SpecificIntegerValue.java
new file mode 100644
index 0000000..0f21bdf
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/SpecificIntegerValue.java
@@ -0,0 +1,388 @@
+/* $Id: SpecificIntegerValue.java,v 1.3 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+/**
+ * This IntegerValue represents a specific integer value.
+ *
+ * @author Eric Lafortune
+ */
+class SpecificIntegerValue extends IntegerValue
+{
+    private int value;
+
+
+    public SpecificIntegerValue(int value)
+    {
+        this.value = value;
+    }
+
+
+    // Implementations for IntegerValue.
+
+    public int value()
+    {
+        return value;
+    }
+
+
+    // Implementations of binary methods of IntegerValue.
+
+    // Perhaps the other value arguments are more specific than apparent
+    // in these methods, so delegate to them.
+
+    public IntegerValue generalize(IntegerValue other)
+    {
+        return other.generalize(this);
+    }
+
+    public IntegerValue add(IntegerValue other)
+    {
+        return other.add(this);
+    }
+
+    public IntegerValue subtract(IntegerValue other)
+    {
+        return other.subtractFrom(this);
+    }
+
+    public IntegerValue subtractFrom(IntegerValue other)
+    {
+        return other.subtract(this);
+    }
+
+    public IntegerValue multiply(IntegerValue other)
+    {
+        return other.multiply(this);
+    }
+
+    public IntegerValue divide(IntegerValue other)
+    throws ArithmeticException
+    {
+        return other.divideOf(this);
+    }
+
+    public IntegerValue divideOf(IntegerValue other)
+    throws ArithmeticException
+    {
+        return other.divide(this);
+    }
+
+    public IntegerValue remainder(IntegerValue other)
+    throws ArithmeticException
+    {
+        return other.remainderOf(this);
+    }
+
+    public IntegerValue remainderOf(IntegerValue other)
+    throws ArithmeticException
+    {
+        return other.remainder(this);
+    }
+
+    public IntegerValue shiftLeft(IntegerValue other)
+    {
+        return other.shiftLeftOf(this);
+    }
+
+    public IntegerValue shiftLeftOf(IntegerValue other)
+    {
+        return other.shiftLeft(this);
+    }
+
+    public IntegerValue shiftRight(IntegerValue other)
+    {
+        return other.shiftRightOf(this);
+    }
+
+    public IntegerValue shiftRightOf(IntegerValue other)
+    {
+        return other.shiftRight(this);
+    }
+
+    public IntegerValue unsignedShiftRight(IntegerValue other)
+    {
+        return other.unsignedShiftRightOf(this);
+    }
+
+    public IntegerValue unsignedShiftRightOf(IntegerValue other)
+    {
+        return other.unsignedShiftRight(this);
+    }
+
+    public LongValue shiftLeftOf(LongValue other)
+    {
+        return other.shiftLeft(this);
+    }
+
+    public LongValue shiftRightOf(LongValue other)
+    {
+        return other.shiftRight(this);
+    }
+
+    public LongValue unsignedShiftRightOf(LongValue other)
+    {
+        return other.unsignedShiftRight(this);
+    }
+
+    public IntegerValue and(IntegerValue other)
+    {
+        return other.and(this);
+    }
+
+    public IntegerValue or(IntegerValue other)
+    {
+        return other.or(this);
+    }
+
+    public IntegerValue xor(IntegerValue other)
+    {
+        return other.xor(this);
+    }
+
+    public int equal(IntegerValue other)
+    {
+        return other.equal(this);
+    }
+
+    public int lessThan(IntegerValue other)
+    {
+        return other.greaterThanOrEqual(this);
+    }
+
+    public int lessThanOrEqual(IntegerValue other)
+    {
+        return other.greaterThan(this);
+    }
+
+
+    // Implementations of unary methods of IntegerValue.
+
+    public IntegerValue negate()
+    {
+        return IntegerValueFactory.create(-value);
+    }
+
+    public IntegerValue convertToByte()
+    {
+        int byteValue = (byte)value;
+
+        return byteValue == value ?
+            this :
+            IntegerValueFactory.create(byteValue);
+    }
+
+    public IntegerValue convertToCharacter()
+    {
+        int charValue = (char)value;
+
+        return charValue == value ?
+            this :
+            IntegerValueFactory.create(charValue);
+    }
+
+    public IntegerValue convertToShort()
+    {
+        int shortValue = (short)value;
+
+        return shortValue == value ?
+            this :
+            IntegerValueFactory.create(shortValue);
+    }
+
+    public IntegerValue convertToInteger()
+    {
+        return this;
+    }
+
+    public LongValue convertToLong()
+    {
+        return LongValueFactory.create((long)value);
+    }
+
+    public FloatValue convertToFloat()
+    {
+        return FloatValueFactory.create((float)value);
+    }
+
+    public DoubleValue convertToDouble()
+    {
+        return DoubleValueFactory.create((double)value);
+    }
+
+
+    // Implementations of binary IntegerValue methods with SpecificIntegerValue
+    // arguments.
+
+    public IntegerValue generalize(SpecificIntegerValue other)
+    {
+        return this.value == other.value ? this : IntegerValueFactory.create();
+    }
+
+    public IntegerValue add(SpecificIntegerValue other)
+    {
+        return IntegerValueFactory.create(this.value + other.value);
+    }
+
+    public IntegerValue subtract(SpecificIntegerValue other)
+    {
+        return IntegerValueFactory.create(this.value - other.value);
+    }
+
+    public IntegerValue subtractFrom(SpecificIntegerValue other)
+    {
+        return IntegerValueFactory.create(other.value - this.value);
+    }
+
+    public IntegerValue multiply(SpecificIntegerValue other)
+    {
+        return IntegerValueFactory.create(this.value * other.value);
+    }
+
+    public IntegerValue divide(SpecificIntegerValue other)
+    throws ArithmeticException
+    {
+        return IntegerValueFactory.create(this.value / other.value);
+    }
+
+    public IntegerValue divideOf(SpecificIntegerValue other)
+    throws ArithmeticException
+    {
+        return IntegerValueFactory.create(other.value / this.value);
+    }
+
+    public IntegerValue remainder(SpecificIntegerValue other)
+    throws ArithmeticException
+    {
+        return IntegerValueFactory.create(this.value % other.value);
+    }
+
+    public IntegerValue remainderOf(SpecificIntegerValue other)
+    throws ArithmeticException
+    {
+        return IntegerValueFactory.create(other.value % this.value);
+    }
+
+    public IntegerValue shiftLeft(SpecificIntegerValue other)
+    {
+        return IntegerValueFactory.create(this.value << other.value);
+    }
+
+    public IntegerValue shiftLeftOf(SpecificIntegerValue other)
+    {
+        return IntegerValueFactory.create(other.value << this.value);
+    }
+
+    public IntegerValue shiftRight(SpecificIntegerValue other)
+    {
+        return IntegerValueFactory.create(this.value >> other.value);
+    }
+
+    public IntegerValue shiftRightOf(SpecificIntegerValue other)
+    {
+        return IntegerValueFactory.create(other.value >> this.value);
+    }
+
+    public IntegerValue unsignedShiftRight(SpecificIntegerValue other)
+    {
+        return IntegerValueFactory.create(this.value >>> other.value);
+    }
+
+    public IntegerValue unsignedShiftRightOf(SpecificIntegerValue other)
+    {
+        return IntegerValueFactory.create(other.value >>> this.value);
+    }
+
+    public LongValue shiftLeftOf(SpecificLongValue other)
+    {
+        return LongValueFactory.create(other.value() << this.value);
+    }
+
+    public LongValue shiftRightOf(SpecificLongValue other)
+    {
+        return LongValueFactory.create(other.value() >> this.value);
+    }
+
+    public LongValue unsignedShiftRightOf(SpecificLongValue other)
+    {
+        return LongValueFactory.create(other.value() >>> this.value);
+    }
+
+    public IntegerValue and(SpecificIntegerValue other)
+    {
+        return IntegerValueFactory.create(this.value & other.value);
+    }
+
+    public IntegerValue or(SpecificIntegerValue other)
+    {
+        return IntegerValueFactory.create(this.value | other.value);
+    }
+
+    public IntegerValue xor(SpecificIntegerValue other)
+    {
+        return IntegerValueFactory.create(this.value ^ other.value);
+    }
+
+    public int equal(SpecificIntegerValue other)
+    {
+        return this.value == other.value ? ALWAYS : NEVER;
+    }
+
+    public int lessThan(SpecificIntegerValue other)
+    {
+        return this.value <  other.value ? ALWAYS : NEVER;
+    }
+
+    public int lessThanOrEqual(SpecificIntegerValue other)
+    {
+        return this.value <= other.value ? ALWAYS : NEVER;
+    }
+
+
+    // Implementations for Value.
+
+    public boolean isSpecific()
+    {
+        return true;
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        return object          != null              &&
+               this.getClass() == object.getClass() &&
+               this.value      == ((SpecificIntegerValue)object).value;
+    }
+
+
+    public int hashCode()
+    {
+        return this.getClass().hashCode() ^ value;
+    }
+
+
+    public String toString()
+    {
+        return "i:"+value;
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/SpecificLongValue.java b/src/proguard/optimize/evaluation/value/SpecificLongValue.java
new file mode 100644
index 0000000..05cc591
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/SpecificLongValue.java
@@ -0,0 +1,258 @@
+/* $Id: SpecificLongValue.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+/**
+ * This LongValue represents a specific long value.
+ *
+ * @author Eric Lafortune
+ */
+class SpecificLongValue extends LongValue
+{
+    private long value;
+
+
+    /**
+     * Creates a new specific long value.
+     */
+    public SpecificLongValue(long value)
+    {
+        this.value = value;
+    }
+
+
+    // Implementations for LongValue.
+
+    public long value()
+    {
+        return value;
+    }
+
+
+    // Implementations of binary methods of LongValue.
+
+    // Perhaps the other value arguments are more specific than apparent
+    // in these methods, so delegate to them.
+
+    public LongValue generalize(LongValue other)
+    {
+        return other.generalize(this);
+    }
+
+    public LongValue add(LongValue other)
+    {
+        return other.add(this);
+    }
+
+    public LongValue subtract(LongValue other)
+    {
+        return other.subtractFrom(this);
+    }
+
+    public LongValue subtractFrom(LongValue other)
+    {
+        return other.subtract(this);
+    }
+
+    public LongValue multiply(LongValue other)
+    {
+        return other.multiply(this);
+    }
+
+    public LongValue divide(LongValue other)
+    {
+        return other.divideOf(this);
+    }
+
+    public LongValue divideOf(LongValue other)
+    {
+        return other.divide(this);
+    }
+
+    public LongValue remainder(LongValue other)
+    {
+        return other.remainderOf(this);
+    }
+
+    public LongValue remainderOf(LongValue other)
+    {
+        return other.remainder(this);
+    }
+
+    public LongValue shiftLeft(IntegerValue other)
+    {
+        return other.shiftLeftOf(this);
+    }
+
+    public LongValue shiftRight(IntegerValue other)
+    {
+        return other.shiftRightOf(this);
+    }
+
+    public LongValue unsignedShiftRight(IntegerValue other)
+    {
+        return other.unsignedShiftRightOf(this);
+    }
+
+    public LongValue and(LongValue other)
+    {
+        return other.and(this);
+    }
+
+    public LongValue or(LongValue other)
+    {
+        return other.or(this);
+    }
+
+    public LongValue xor(LongValue other)
+    {
+        return other.xor(this);
+    }
+
+    public IntegerValue compare(LongValue other)
+    {
+        return other.compareReverse(this);
+    }
+
+
+    // Implementations of unary methods of LongValue.
+
+    public LongValue negate()
+    {
+        return LongValueFactory.create(-value);
+    }
+
+    public IntegerValue convertToInteger()
+    {
+        return IntegerValueFactory.create((int)value);
+    }
+
+    public FloatValue convertToFloat()
+    {
+        return FloatValueFactory.create((float)value);
+    }
+
+    public DoubleValue convertToDouble()
+    {
+        return DoubleValueFactory.create((double)value);
+    }
+
+
+    // Implementations of binary LongValue methods with SpecificLongValue
+    // arguments.
+
+    public LongValue generalize(SpecificLongValue other)
+    {
+        return this.value == other.value ? this : LongValueFactory.create();
+    }
+
+    public LongValue add(SpecificLongValue other)
+    {
+        return LongValueFactory.create(this.value + other.value);
+    }
+
+    public LongValue subtract(SpecificLongValue other)
+    {
+        return LongValueFactory.create(this.value - other.value);
+    }
+
+    public LongValue subtractFrom(SpecificLongValue other)
+    {
+        return LongValueFactory.create(other.value - this.value);
+    }
+
+    public LongValue multiply(SpecificLongValue other)
+    {
+        return LongValueFactory.create(this.value * other.value);
+    }
+
+    public LongValue divide(SpecificLongValue other)
+    {
+        return LongValueFactory.create(this.value / other.value);
+    }
+
+    public LongValue divideOf(SpecificLongValue other)
+    {
+        return LongValueFactory.create(other.value / this.value);
+    }
+
+    public LongValue remainder(SpecificLongValue other)
+    {
+        return LongValueFactory.create(this.value % other.value);
+    }
+
+    public LongValue remainderOf(SpecificLongValue other)
+    {
+        return LongValueFactory.create(other.value % this.value);
+    }
+
+    public LongValue and(SpecificLongValue other)
+    {
+        return LongValueFactory.create(this.value & other.value);
+    }
+
+    public LongValue or(SpecificLongValue other)
+    {
+        return LongValueFactory.create(this.value | other.value);
+    }
+
+    public LongValue xor(SpecificLongValue other)
+    {
+        return LongValueFactory.create(this.value ^ other.value);
+    }
+
+    public IntegerValue compare(SpecificLongValue other)
+    {
+        return this.value <  other.value ? IntegerValueFactory.create(-1) :
+               this.value == other.value ? IntegerValueFactory.create(0) :
+                                           IntegerValueFactory.create(1);
+    }
+
+
+    // Implementations for Value.
+
+    public boolean isSpecific()
+    {
+        return true;
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        return object          != null              &&
+               this.getClass() == object.getClass() &&
+               this.value      == ((SpecificLongValue)object).value;
+    }
+
+
+    public int hashCode()
+    {
+        return this.getClass().hashCode() ^ (int)value;
+    }
+
+
+    public String toString()
+    {
+        return "l:"+value;
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/SpecificReferenceValue.java b/src/proguard/optimize/evaluation/value/SpecificReferenceValue.java
new file mode 100644
index 0000000..e31c969
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/SpecificReferenceValue.java
@@ -0,0 +1,158 @@
+/* $Id: SpecificReferenceValue.java,v 1.4 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+import proguard.classfile.*;
+import proguard.classfile.ClassCpInfo;
+
+/**
+ * This ReferenceValue represents a reference value of a specific type.
+ *
+ * @author Eric Lafortune
+ */
+class SpecificReferenceValue extends ReferenceValue
+{
+    protected ClassFile value;
+
+
+    /**
+     * Creates a new specific reference value.
+     */
+    public SpecificReferenceValue(ClassFile value, boolean mayBeNull)
+    {
+        super(mayBeNull);
+
+        this.value = value;
+    }
+
+
+    // Implementations for ReferenceValue.
+
+    public ClassFile value()
+    {
+        return value;
+    }
+
+
+    // Implementations of binary methods of ReferenceValue.
+
+    // Perhaps the other value arguments are more specific than apparent
+    // in these methods, so delegate to them.
+
+    public ReferenceValue generalize(ReferenceValue other)
+    {
+        return other.generalize(this);
+    }
+
+    public int equal(ReferenceValue other)
+    {
+        return other.equal(this);
+    }
+
+
+    // Implementations of unary methods of ReferenceValue.
+
+    public int isNull()
+    {
+        return value == null ? ALWAYS :
+               mayBeNull     ? MAYBE  :
+                               NEVER;
+    }
+
+
+    public int instanceOf(ClassFile typeClassFile, int typeDimension)
+    {
+        // If this value is null, it is never an instance of any class.
+        if (value == null)
+        {
+            return NEVER;
+        }
+
+        // If the type class is unknown or the type is an array type, we can't
+        // tell for sure.
+        if (typeClassFile == null ||
+            typeDimension > 0)
+        {
+            return MAYBE;
+        }
+
+        // If the value extends the type, we're sure.
+        return value.extends_(typeClassFile) ||
+               value.implements_(typeClassFile) ?
+                   ALWAYS :
+                   MAYBE;
+    }
+
+
+    // Implementations of binary ReferenceValue methods with SpecificReferenceValue
+    // arguments.
+
+    public ReferenceValue generalize(SpecificReferenceValue other)
+    {
+        return this.value == other.value ?
+            this.mayBeNull ? this : other :
+            ReferenceValueFactory.create(this.mayBeNull || other.mayBeNull);
+    }
+
+    public int equal(SpecificReferenceValue other)
+    {
+        return this.value  == null &&
+               other.value == null ? ALWAYS : MAYBE;
+    }
+
+
+    // Implementations for Value.
+
+    public boolean isSpecific()
+    {
+        return true;
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (object == null ||
+            this.getClass() != object.getClass())
+        {
+            return false;
+        }
+
+        SpecificReferenceValue other = (SpecificReferenceValue)object;
+        return this.mayBeNull == other.mayBeNull &&
+               (this.value == null ? other.value == null :
+                                     this.value.equals(other.value));
+    }
+
+
+    public int hashCode()
+    {
+        return this.getClass().hashCode() ^
+               (value == null ? 0 : value.hashCode());
+    }
+
+
+    public String toString()
+    {
+        return "a:" + (value == null ? "null" : value.getName());
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/Value.java b/src/proguard/optimize/evaluation/value/Value.java
new file mode 100644
index 0000000..12affa1
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/Value.java
@@ -0,0 +1,143 @@
+/* $Id: Value.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+/**
+ * This abstract class represents a partially evaluated value.
+ *
+ * @author Eric Lafortune
+ */
+public abstract class Value
+{
+    public static final int NEVER  = -1;
+    public static final int MAYBE  = 0;
+    public static final int ALWAYS = 1;
+
+    public static final int TYPE_INTEGER            = 1;
+    public static final int TYPE_LONG               = 2;
+    public static final int TYPE_FLOAT              = 3;
+    public static final int TYPE_DOUBLE             = 4;
+    public static final int TYPE_REFERENCE          = 5;
+    public static final int TYPE_INSTRUCTION_OFFSET = 6;
+
+
+    /**
+     * Returns this Value as a Category1Value.
+     */
+    public Category1Value category1Value()
+    {
+        throw new IllegalArgumentException("Value is not a Category 1 value [" + this.getClass().getName() + "]");
+    }
+
+    /**
+     * Returns this Value as a Category2Value.
+     */
+    public Category2Value category2Value()
+    {
+        throw new IllegalArgumentException("Value is not a Category 2 value [" + this.getClass().getName() + "]");
+    }
+
+
+    /**
+     * Returns this Value as an IntegerValue.
+     */
+    public IntegerValue integerValue()
+    {
+        throw new IllegalArgumentException("Value is not an integer value [" + this.getClass().getName() + "]");
+    }
+
+    /**
+     * Returns this Value as a LongValue.
+     */
+    public LongValue longValue()
+    {
+        throw new IllegalArgumentException("Value is not a long value [" + this.getClass().getName() + "]");
+    }
+
+    /**
+     * Returns this Value as a FloatValue.
+     */
+    public FloatValue floatValue()
+    {
+        throw new IllegalArgumentException("Value is not a float value [" + this.getClass().getName() + "]");
+    }
+
+    /**
+     * Returns this Value as a DoubleValue.
+     */
+    public DoubleValue doubleValue()
+    {
+        throw new IllegalArgumentException("Value is not a double value [" + this.getClass().getName() + "]");
+    }
+
+    /**
+     * Returns this Value as a ReferenceValue.
+     */
+    public ReferenceValue referenceValue()
+    {
+        throw new IllegalArgumentException("Value is not a reference value [" + this.getClass().getName() + "]");
+    }
+
+    /**
+     * Returns this Value as a InstructionOffsetValue.
+     */
+    public InstructionOffsetValue instructionOffsetValue()
+    {
+        throw new IllegalArgumentException("Value is not an instruction offset value [" + this.getClass().getName() + "]");
+    }
+
+
+    /**
+     * Returns whether this Value represents a single specific value.
+     */
+    public boolean isSpecific()
+    {
+        return false;
+    }
+
+
+    /**
+     * Returns the generalization of this Value and the given other Value.
+     */
+    public abstract Value generalize(Value other);
+
+
+    /**
+     * Returns whether the computational type of this Value is a category 2 type.
+     * This means that it takes up the space of two category 1 types on the
+     * stack, for instance.
+     */
+    public abstract boolean isCategory2();
+
+
+    /**
+     * Returns the computational type of this Value. The type is one of
+     * <ul>
+     * <li><code>TYPE_INTEGER</code>
+     * <li><code>TYPE_LONG</code>
+     * <li><code>TYPE_FLOAT</code>
+     * <li><code>TYPE_DOUBLE</code>
+     * <li><code>TYPE_REFERENCE</code>
+     * <li><code>TYPE_INSTRUCTION_OFFSET</code>
+     * </ul>
+     */
+    public abstract int computationalType();
+}
diff --git a/src/proguard/optimize/evaluation/value/ValueFactory.java b/src/proguard/optimize/evaluation/value/ValueFactory.java
new file mode 100644
index 0000000..c4149e6
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/ValueFactory.java
@@ -0,0 +1,63 @@
+/* $Id: ValueFactory.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation.value;
+
+import proguard.classfile.ClassConstants;
+
+/**
+ * This class provides methods to create and reuse Value objects.
+ *
+ * @author Eric Lafortune
+ */
+public class ValueFactory
+{
+    /**
+     * Creates a new Value with an undefined value, corresponding to the given
+     * internal type. The void type returns <code>null</code>.
+     */
+    public static Value create(String internalType)
+    {
+        switch (internalType.charAt(0))
+        {
+            case ClassConstants.INTERNAL_TYPE_VOID:
+                return null;
+
+            case ClassConstants.INTERNAL_TYPE_BOOLEAN:
+            case ClassConstants.INTERNAL_TYPE_BYTE:
+            case ClassConstants.INTERNAL_TYPE_CHAR:
+            case ClassConstants.INTERNAL_TYPE_SHORT:
+            case ClassConstants.INTERNAL_TYPE_INT:
+                return IntegerValueFactory.create();
+
+            case ClassConstants.INTERNAL_TYPE_FLOAT:
+                return FloatValueFactory.create();
+
+            case ClassConstants.INTERNAL_TYPE_LONG:
+                return LongValueFactory.create();
+
+            case ClassConstants.INTERNAL_TYPE_DOUBLE:
+                return DoubleValueFactory.create();
+
+            default:
+                return ReferenceValueFactory.create(true);
+        }
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/package.html b/src/proguard/optimize/evaluation/value/package.html
new file mode 100644
index 0000000..71e1b13
--- /dev/null
+++ b/src/proguard/optimize/evaluation/value/package.html
@@ -0,0 +1,3 @@
+<body>
+This package contains classes that represent partial evaluation values.
+</body>
diff --git a/src/proguard/optimize/package.html b/src/proguard/optimize/package.html
new file mode 100644
index 0000000..3ee1353
--- /dev/null
+++ b/src/proguard/optimize/package.html
@@ -0,0 +1,4 @@
+<body>
+This package contains visitors that assist with various optimizations of byte
+code.
+</body>
diff --git a/src/proguard/optimize/peephole/BranchTargetFinder.java b/src/proguard/optimize/peephole/BranchTargetFinder.java
new file mode 100644
index 0000000..15bb005
--- /dev/null
+++ b/src/proguard/optimize/peephole/BranchTargetFinder.java
@@ -0,0 +1,178 @@
+/* $Id: BranchTargetFinder.java,v 1.5 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This AttrInfoVisitor finds all branch targets in the CodeAttrInfo objects
+ * that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class BranchTargetFinder
+implements   AttrInfoVisitor,
+             InstructionVisitor,
+             ExceptionInfoVisitor
+{
+    private boolean[] isBranchTarget;
+
+
+    /**
+     * Creates a new BranchTargetFinder.
+     * @param codeLength an estimate of the maximum length of all the code that
+     *                   will be edited.
+     */
+    public BranchTargetFinder(int codeLength)
+    {
+        isBranchTarget = new boolean[codeLength + 1];
+    }
+
+
+    /**
+     * Returns whether the instruction at the given offset is the target of a
+     * branch instruction in the CodeAttrInfo that was visited most recently.
+     */
+    public boolean isBranchTarget(int offset)
+    {
+        return isBranchTarget[offset];
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
+
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        // Make sure there is a sufficiently large boolean array.
+        int length = codeAttrInfo.u4codeLength + 1;
+        if (isBranchTarget.length < length)
+        {
+            // Create a new boolean array.
+            isBranchTarget = new boolean[length];
+        }
+        else
+        {
+            // Reset the boolean array.
+            for (int index = 0; index < length; index++)
+            {
+                isBranchTarget[index] = false;
+            }
+        }
+
+        // The first instruction and the end of the code are always branch targets.
+        isBranchTarget[0]                         = true;
+        isBranchTarget[codeAttrInfo.u4codeLength] = true;
+
+        // Mark branch targets by going over all instructions.
+        codeAttrInfo.instructionsAccept(classFile, methodInfo, this);
+
+        // Mark branch targets in the exception table.
+        codeAttrInfo.exceptionsAccept(classFile, methodInfo, this);
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction) {}
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
+
+
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+    {
+        // Mark the branch target.
+        isBranchTarget[offset + branchInstruction.branchOffset] = true;
+    }
+
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
+    {
+        // Mark the branch targets of the default jump offset.
+        isBranchTarget[offset + tableSwitchInstruction.defaultOffset] = true;
+
+        // Mark the branch targets of the jump offsets.
+        markBranchTargets(offset,
+                          tableSwitchInstruction.jumpOffsets,
+                          tableSwitchInstruction.highCase -
+                          tableSwitchInstruction.lowCase + 1);
+    }
+
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
+    {
+        // Mark the branch targets of the default jump offset.
+        isBranchTarget[offset + lookUpSwitchInstruction.defaultOffset] = true;
+
+        // Mark the branch targets of the jump offsets.
+        markBranchTargets(offset,
+                          lookUpSwitchInstruction.jumpOffsets,
+                          lookUpSwitchInstruction.jumpOffsetCount);
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo)
+    {
+        // Remap the code offsets. Note that the branch target array also has
+        // an entry for the first offset after the code, for u2endpc.
+        isBranchTarget[exceptionInfo.u2startpc]   = true;
+        isBranchTarget[exceptionInfo.u2endpc]     = true;
+        isBranchTarget[exceptionInfo.u2handlerpc] = true;
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Marks the branch targets of the given jump offsets for the instruction
+     * at the given offset.
+     */
+    private void markBranchTargets(int offset, int[] jumpOffsets, int length)
+    {
+        for (int index = 0; index < length; index++)
+        {
+            isBranchTarget[offset + jumpOffsets[index]] = true;
+        }
+    }
+}
diff --git a/src/proguard/optimize/peephole/ClassFileFinalizer.java b/src/proguard/optimize/peephole/ClassFileFinalizer.java
new file mode 100644
index 0000000..88c1fb2
--- /dev/null
+++ b/src/proguard/optimize/peephole/ClassFileFinalizer.java
@@ -0,0 +1,96 @@
+/* $Id: ClassFileFinalizer.java,v 1.6 2004/12/19 21:03:13 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.*;
+import proguard.classfile.util.MemberFinder;
+import proguard.classfile.visitor.*;
+import proguard.optimize.*;
+
+/**
+ * This <code>ClassFileVisitor</code> and <code>MemberInfoVisitor</code>
+ * makes the class files it visits, and their class members, final, if possible.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassFileFinalizer
+  implements ClassFileVisitor,
+             MemberInfoVisitor
+{
+    private MemberFinder memberFinder = new MemberFinder();
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // If the class is not final/interface/abstract,
+        // and it is not being kept,
+        // then make it final.
+        if ((programClassFile.u2accessFlags & (ClassConstants.INTERNAL_ACC_FINAL     |
+                                               ClassConstants.INTERNAL_ACC_INTERFACE |
+                                               ClassConstants.INTERNAL_ACC_ABSTRACT)) == 0 &&
+            !KeepMarker.isKept(programClassFile)                                           &&
+            programClassFile.subClasses == null)
+        {
+            programClassFile.u2accessFlags |= ClassConstants.INTERNAL_ACC_FINAL;
+        }
+
+        // Check all methods.
+        programClassFile.methodsAccept(this);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile) {}
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        String name = programMethodInfo.getName(programClassFile);
+
+        // If the method is not final/abstract/private.
+        // and it is not an initialization method,
+        // and its class is final,
+        //     or it is not being kept and it is not overridden,
+        // then make it final.
+        if ((programMethodInfo.u2accessFlags & (ClassConstants.INTERNAL_ACC_FINAL    |
+                                                ClassConstants.INTERNAL_ACC_ABSTRACT |
+                                                ClassConstants.INTERNAL_ACC_PRIVATE)) == 0 &&
+            !name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT)                        &&
+            !name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)                          &&
+            ((programClassFile.u2accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0 ||
+             (!KeepMarker.isKept(programMethodInfo) &&
+              (programClassFile.subClasses == null ||
+               !memberFinder.isOverriden(programClassFile, programMethodInfo)))))
+        {
+            programMethodInfo.u2accessFlags |= ClassConstants.INTERNAL_ACC_FINAL;
+        }
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+}
diff --git a/src/proguard/optimize/peephole/GetterSetterInliner.java b/src/proguard/optimize/peephole/GetterSetterInliner.java
new file mode 100644
index 0000000..caef29c
--- /dev/null
+++ b/src/proguard/optimize/peephole/GetterSetterInliner.java
@@ -0,0 +1,387 @@
+/* $Id: GetterSetterInliner.java,v 1.14 2004/12/18 20:22:42 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.util.*;
+import proguard.classfile.editor.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This InstructionVisitor inlines simple getter and setter methods.
+ *
+ * @author Eric Lafortune
+ */
+public class GetterSetterInliner
+implements   InstructionVisitor,
+             CpInfoVisitor
+{
+    private static final String SETTER_RETURN_TYPE = "V";
+
+    private ConstantPoolEditor    constantPoolEditor  = new ConstantPoolEditor();
+    private MyGetterSetterChecker getterSetterChecker = new MyGetterSetterChecker();
+    private MemberFinder          memberFinder        = new MemberFinder();
+
+    private CodeAttrInfoEditor codeAttrInfoEditor;
+    private boolean            allowAccessModification;
+
+
+    // Return values of the getter/setter checker.
+    private byte        getFieldPutFieldOpcode;
+    private int         referencedClassIndex;
+    private int         referencedFieldIndex;
+    private ClassFile   referencedClassFile;
+    private MemberInfo  referencedFieldInfo;
+    private ClassFile[] typeReferencedClassFiles;
+
+
+    /**
+     * Creates a new GetterSetterInliner.
+     * @param codeAttrInfoEditor a code editor that can be used for
+     *                           accumulating changes to the code.
+     * @param allowAccessModification indicates whether the access modifiers of
+     *                                a field can be changed in order to inline
+     *                                its getter or setter.
+     */
+    public GetterSetterInliner(CodeAttrInfoEditor codeAttrInfoEditor,
+                               boolean            allowAccessModification)
+    {
+        this.codeAttrInfoEditor      = codeAttrInfoEditor;
+        this.allowAccessModification = allowAccessModification;
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+
+
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    {
+        byte opcode = cpInstruction.opcode;
+
+        // Is this instruction a non-static invoke instruction?
+        if (opcode == InstructionConstants.OP_INVOKEVIRTUAL ||
+            opcode == InstructionConstants.OP_INVOKESPECIAL)
+        {
+            // Check if it's a getter or setter that can be inlined.
+            getFieldPutFieldOpcode = 0;
+
+            classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
+
+            // Do we have a getfield or putfield instruction to inline?
+            if (getFieldPutFieldOpcode != 0)
+            {
+                // Reuse or create the field reference in this class.
+                int fieldrefCpInfoIndex = classFile.equals(referencedClassFile) ?
+                    referencedFieldIndex :
+                    constantPoolEditor.addFieldrefCpInfo((ProgramClassFile)classFile,
+                                                         referencedClassIndex,
+                                                         referencedFieldInfo.getName(referencedClassFile),
+                                                         referencedFieldInfo.getDescriptor(referencedClassFile),
+                                                         referencedClassFile,
+                                                         referencedFieldInfo,
+                                                         typeReferencedClassFiles);
+
+                // Inline the getfield or putfield instruction.
+                Instruction replacementInstruction = new CpInstruction(getFieldPutFieldOpcode,
+                                                                       fieldrefCpInfoIndex).shrink();
+
+                codeAttrInfoEditor.replaceInstruction(offset, replacementInstruction);
+            }
+        }
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+
+
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    {
+        String descriptor = methodrefCpInfo.getType(classFile);
+
+        // A getter or a setter can't have more than one parameter.
+        int parameterCount = ClassUtil.internalMethodParameterCount(descriptor);
+        if (parameterCount > 1)
+        {
+            return;
+        }
+
+        // A getter must return a value, a setter must return void.
+        String returnType = ClassUtil.internalMethodReturnType(descriptor);
+        if ((parameterCount > 0) ^ returnType.equals(SETTER_RETURN_TYPE))
+        {
+            return;
+        }
+
+        // The referenced method must be present and private or final.
+        MemberInfo referencedMethodInfo = methodrefCpInfo.referencedMemberInfo;
+        if (referencedMethodInfo == null ||
+            (referencedMethodInfo.getAccessFlags() &
+             (ClassConstants.INTERNAL_ACC_PRIVATE |
+              ClassConstants.INTERNAL_ACC_FINAL)) == 0)
+        {
+            return;
+        }
+
+        // Check if the method can be inlined.
+        referencedMethodInfo.accept(methodrefCpInfo.referencedClassFile,
+                                    getterSetterChecker);
+
+        // Do we have a getfield or putfield instruction to inline?
+        if (getFieldPutFieldOpcode == 0)
+        {
+            return;
+        }
+
+        // Remember the constant pool index of the referenced class.
+        referencedClassIndex = methodrefCpInfo.u2classIndex;
+
+        // Doesn't the field allow at least the same access as the getter or
+        // setter?
+        if (AccessUtil.accessLevel(referencedFieldInfo.getAccessFlags()) <
+            AccessUtil.accessLevel(referencedMethodInfo.getAccessFlags()))
+        {
+            // Are we allowed to fix the access?
+            if (allowAccessModification)
+            {
+                // Is the field access private, and is the field shadowed by
+                // a field in a subclass?
+                if (AccessUtil.accessLevel(referencedFieldInfo.getAccessFlags()) == AccessUtil.PRIVATE &&
+                    memberFinder.isShadowed(referencedClassFile, (FieldInfo)referencedFieldInfo))
+                {
+                    // Cancel the inlining.
+                    getFieldPutFieldOpcode = 0;
+                }
+
+                if (getFieldPutFieldOpcode != 0)
+                {
+                    // Fix the access.
+                    ((ProgramFieldInfo)referencedFieldInfo).u2accessFlags =
+                        AccessUtil.replaceAccessFlags(referencedFieldInfo.getAccessFlags(),
+                                                      referencedMethodInfo.getAccessFlags());
+                }
+            }
+            else
+            {
+                // We can't give the field the access of the getter or setter.
+                // Forget about inlining it after all.
+                getFieldPutFieldOpcode = 0;
+            }
+        }
+    }
+
+
+    /**
+     * This MemberInfoVisitor checks whether the visited member is a simple
+     * getter or setter.
+     */
+    private class MyGetterSetterChecker
+    implements    MemberInfoVisitor,
+                  AttrInfoVisitor,
+                  InstructionVisitor,
+                  CpInfoVisitor
+    {
+        // Implementations for MemberInfoVisitor.
+
+        public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
+
+        public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+        {
+            programMethodInfo.attributesAccept(programClassFile, this);
+        }
+
+
+        public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+        public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+
+
+        // Implementations for AttrInfoVisitor.
+
+        public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
+        public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
+        public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
+        public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
+        public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
+        public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
+        public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
+        public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
+        public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
+        public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
+        public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
+        public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
+        public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
+        public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
+        public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
+        public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
+        public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
+        public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
+
+
+        public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+        {
+            byte[] code = codeAttrInfo.code;
+
+            int         offset = 0;
+            Instruction instruction;
+
+            // TODO: Handle static getters and setters.
+
+            // Check the first instruction.
+            instruction = InstructionFactory.create(code, offset);
+            if (instruction.opcode != InstructionConstants.OP_ALOAD_0)
+            {
+                return;
+            }
+
+            offset += instruction.length(offset);
+
+            // Start remembering if this is a getter or a setter.
+            boolean getter = true;
+
+            // TODO: Handle array getters and setters.
+
+            // Check the second instruction.
+            instruction = InstructionFactory.create(code, offset);
+            byte opcode = instruction.opcode;
+            if (opcode == InstructionConstants.OP_ILOAD_1 ||
+                opcode == InstructionConstants.OP_LLOAD_1 ||
+                opcode == InstructionConstants.OP_FLOAD_1 ||
+                opcode == InstructionConstants.OP_DLOAD_1 ||
+                opcode == InstructionConstants.OP_ALOAD_1)
+            {
+                // This instruction is loading the argument. Skip it.
+                offset += instruction.length(offset);
+
+                instruction = InstructionFactory.create(code, offset);
+                opcode      = instruction.opcode;
+
+                // So this is a setter.
+                getter = false;
+            }
+
+            // Check the next instruction.
+            if (getter ? opcode != InstructionConstants.OP_GETFIELD :
+                         opcode != InstructionConstants.OP_PUTFIELD)
+            {
+                return;
+            }
+
+            // Retrieve the field class, name and type.
+            instruction.accept(classFile, methodInfo, codeAttrInfo, offset, this);
+
+            // Check the class.
+            // TODO: Handle fields that are in super classes.
+            if (!classFile.equals(referencedClassFile))
+            {
+                return;
+            }
+
+            offset += instruction.length(offset);
+
+            // Remember the instruction opcode.
+            getFieldPutFieldOpcode = opcode;
+
+            // Check the last instruction.
+            instruction = InstructionFactory.create(code, offset);
+            opcode      = instruction.opcode;
+            if (getter ? opcode != InstructionConstants.OP_IRETURN &&
+                         opcode != InstructionConstants.OP_LRETURN &&
+                         opcode != InstructionConstants.OP_FRETURN &&
+                         opcode != InstructionConstants.OP_DRETURN &&
+                         opcode != InstructionConstants.OP_ARETURN :
+                         opcode != InstructionConstants.OP_RETURN)
+            {
+                // This isn't a simple getter or setter.
+                // Forget about inlining it.
+                getFieldPutFieldOpcode = 0;
+            }
+        }
+
+
+        // Implementations for InstructionVisitor.
+
+        public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
+        public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
+        public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
+        public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
+        public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+
+
+        public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+        {
+            // Remember the index of the field reference.
+            referencedFieldIndex = cpInstruction.cpIndex;
+
+            // Retrieve the referenced field and its class file.
+            classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
+        }
+
+
+        // Implementations for CpInfoVisitor.
+
+        public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+        public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+        public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+        public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+        public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
+        public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+        public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
+        public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
+        public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
+
+
+        public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+        {
+            // Remember the class file and the field.
+            referencedClassFile = fieldrefCpInfo.referencedClassFile;
+            referencedFieldInfo = fieldrefCpInfo.referencedMemberInfo;
+
+            // Retrieve the referenced class files.
+            classFile.constantPoolEntryAccept(fieldrefCpInfo.u2nameAndTypeIndex, this);
+        }
+
+
+        public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo)
+        {
+            // Remember the referenced class files of the field.
+            typeReferencedClassFiles = nameAndTypeCpInfo.referencedClassFiles;
+        }
+    }
+}
diff --git a/src/proguard/optimize/peephole/GotoReturnReplacer.java b/src/proguard/optimize/peephole/GotoReturnReplacer.java
new file mode 100644
index 0000000..c472e2d
--- /dev/null
+++ b/src/proguard/optimize/peephole/GotoReturnReplacer.java
@@ -0,0 +1,92 @@
+/* $Id: GotoReturnReplacer.java,v 1.6 2004/11/20 15:06:55 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.editor.*;
+import proguard.classfile.instruction.*;
+
+/**
+ * This InstructionVisitor replaces unconditional branches to return instructions
+ * by these same return instructions.
+ *
+ * @author Eric Lafortune
+ */
+public class GotoReturnReplacer implements InstructionVisitor
+{
+    private CodeAttrInfoEditor codeAttrInfoEditor;
+
+
+    /**
+     * Creates a new GotoReturnReplacer.
+     * @param codeAttrInfoEditor a code editor that can be used for
+     *                           accumulating changes to the code.
+     */
+    public GotoReturnReplacer(CodeAttrInfoEditor codeAttrInfoEditor)
+    {
+        this.codeAttrInfoEditor = codeAttrInfoEditor;
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction) {}
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+
+
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+    {
+        // Check if the instruction is an unconditional goto instruction.
+        byte opcode = branchInstruction.opcode;
+        if (opcode == InstructionConstants.OP_GOTO ||
+            opcode == InstructionConstants.OP_GOTO_W)
+        {
+            // Check if the goto instruction points to a return instruction.
+            int targetOffset = offset + branchInstruction.branchOffset;
+
+            if (!codeAttrInfoEditor.isModified(offset) &&
+                !codeAttrInfoEditor.isModified(targetOffset))
+            {
+                Instruction targetInstruction = InstructionFactory.create(codeAttrInfo.code,
+                                                                          targetOffset);
+                switch (targetInstruction.opcode)
+                {
+                    case InstructionConstants.OP_IRETURN:
+                    case InstructionConstants.OP_LRETURN:
+                    case InstructionConstants.OP_FRETURN:
+                    case InstructionConstants.OP_DRETURN:
+                    case InstructionConstants.OP_ARETURN:
+                    case InstructionConstants.OP_RETURN:
+                        // Replace the goto instruction by the return instruction.
+                        Instruction returnInstruction =
+                             new SimpleInstruction(targetInstruction.opcode);
+                        codeAttrInfoEditor.replaceInstruction(offset,
+                                                              returnInstruction);
+                        break;
+                }
+            }
+        }
+    }
+}
diff --git a/src/proguard/optimize/peephole/LoadStoreRemover.java b/src/proguard/optimize/peephole/LoadStoreRemover.java
new file mode 100644
index 0000000..a4619f4
--- /dev/null
+++ b/src/proguard/optimize/peephole/LoadStoreRemover.java
@@ -0,0 +1,99 @@
+/* $Id: LoadStoreRemover.java,v 1.6 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.editor.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This InstructionVisitor deletes load/store instruction pairs.
+ *
+ * @author Eric Lafortune
+ */
+public class LoadStoreRemover implements InstructionVisitor
+{
+    private BranchTargetFinder branchTargetFinder;
+    private CodeAttrInfoEditor codeAttrInfoEditor;
+
+
+    /**
+     * Creates a new LoadStoreRemover.
+     * @param branchTargetFinder a branch target finder that has been
+     *                           initialized to indicate branch targets
+     *                           in the visited code.
+     * @param codeAttrInfoEditor a code editor that can be used for
+     *                           accumulating changes to the code.
+     */
+    public LoadStoreRemover(BranchTargetFinder branchTargetFinder,
+                             CodeAttrInfoEditor codeAttrInfoEditor)
+    {
+        this.branchTargetFinder = branchTargetFinder;
+        this.codeAttrInfoEditor = codeAttrInfoEditor;
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction) {}
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+
+
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    {
+        // Is this instruction a load instruction?
+        if (variableInstruction.isLoad() &&
+            variableInstruction.opcode != InstructionConstants.OP_RET)
+        {
+            byte opcode        = variableInstruction.opcode;
+            int  variableIndex = variableInstruction.variableIndex;
+
+            int nextOffset = offset + variableInstruction.length(offset);
+
+            if (!codeAttrInfoEditor.isModified(offset)     &&
+                !codeAttrInfoEditor.isModified(nextOffset) &&
+                !branchTargetFinder.isBranchTarget(nextOffset))
+            {
+                // Is the next instruction a corresponding store instruction?
+                Instruction nextInstruction = InstructionFactory.create(codeAttrInfo.code,
+                                                                        nextOffset);
+
+                if (nextInstruction instanceof VariableInstruction)
+                {
+                    variableInstruction = (VariableInstruction)nextInstruction;
+                    if (!variableInstruction.isLoad()                              &&
+                        variableInstruction.opcode != InstructionConstants.OP_IINC &&
+                        variableInstruction.variableIndex == variableIndex)
+                    {
+                        // Delete both instructions.
+                        codeAttrInfoEditor.deleteInstruction(offset);
+                        codeAttrInfoEditor.deleteInstruction(nextOffset);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/proguard/optimize/peephole/NopRemover.java b/src/proguard/optimize/peephole/NopRemover.java
new file mode 100644
index 0000000..c21bb0f
--- /dev/null
+++ b/src/proguard/optimize/peephole/NopRemover.java
@@ -0,0 +1,68 @@
+/* $Id: NopRemover.java,v 1.5 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.editor.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This InstructionVisitor removes all nop instructions that it encounters.
+ *
+ * @author Eric Lafortune
+ */
+public class NopRemover implements InstructionVisitor
+{
+    private CodeAttrInfoEditor codeAttrInfoEditor;
+
+
+    /**
+     * Creates a new NopRemover.
+     * @param codeAttrInfoEditor a code editor that can be used for
+     *                           accumulating changes to the code.
+     */
+    public NopRemover(CodeAttrInfoEditor codeAttrInfoEditor)
+    {
+        this.codeAttrInfoEditor = codeAttrInfoEditor;
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction) {}
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
+    {
+        // Check if the instruction is a nop instruction.
+        if (simpleInstruction.opcode == InstructionConstants.OP_NOP &&
+            !codeAttrInfoEditor.isModified(offset))
+        {
+            codeAttrInfoEditor.deleteInstruction(offset);
+        }
+    }
+}
diff --git a/src/proguard/optimize/peephole/PushPopRemover.java b/src/proguard/optimize/peephole/PushPopRemover.java
new file mode 100644
index 0000000..c342f68
--- /dev/null
+++ b/src/proguard/optimize/peephole/PushPopRemover.java
@@ -0,0 +1,139 @@
+/* $Id: PushPopRemover.java,v 1.8 2004/10/10 20:56:58 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.editor.*;
+import proguard.classfile.instruction.*;
+
+/**
+ * This InstructionVisitor deletes all push/pop instruction pairs. In this
+ * context, push instructions are instructions that push values onto the stack,
+ * like dup and load instructions.
+ *
+ * @author Eric Lafortune
+ */
+public class PushPopRemover implements InstructionVisitor
+{
+    private BranchTargetFinder branchTargetFinder;
+    private CodeAttrInfoEditor codeAttrInfoEditor;
+
+
+    /**
+     * Creates a new PushPopRemover.
+     * @param branchTargetFinder a branch target finder that has been
+     *                           initialized to indicate branch targets
+     *                           in the visited code.
+     * @param codeAttrInfoEditor a code editor that can be used for
+     *                           accumulating changes to the code.
+     */
+    public PushPopRemover(BranchTargetFinder branchTargetFinder,
+                          CodeAttrInfoEditor codeAttrInfoEditor)
+    {
+        this.branchTargetFinder = branchTargetFinder;
+        this.codeAttrInfoEditor = codeAttrInfoEditor;
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction) {}
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
+    {
+        switch (simpleInstruction.opcode)
+        {
+            case InstructionConstants.OP_ICONST_M1:
+            case InstructionConstants.OP_ICONST_0:
+            case InstructionConstants.OP_ICONST_1:
+            case InstructionConstants.OP_ICONST_2:
+            case InstructionConstants.OP_ICONST_3:
+            case InstructionConstants.OP_ICONST_4:
+            case InstructionConstants.OP_ICONST_5:
+            case InstructionConstants.OP_LCONST_0:
+            case InstructionConstants.OP_LCONST_1:
+            case InstructionConstants.OP_FCONST_0:
+            case InstructionConstants.OP_FCONST_1:
+            case InstructionConstants.OP_FCONST_2:
+            case InstructionConstants.OP_DCONST_0:
+            case InstructionConstants.OP_DCONST_1:
+
+            case InstructionConstants.OP_DUP:
+            case InstructionConstants.OP_DUP2:
+            case InstructionConstants.OP_BIPUSH:
+            case InstructionConstants.OP_SIPUSH:
+            case InstructionConstants.OP_LDC:
+            case InstructionConstants.OP_LDC_W:
+            case InstructionConstants.OP_LDC2_W:
+                // All these simple instructions are pushing instructions.
+                deleteWithSubsequentPop(codeAttrInfo, offset, simpleInstruction);
+                break;
+        }
+    }
+
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    {
+        if (variableInstruction.isLoad() &&
+            variableInstruction.opcode != InstructionConstants.OP_RET)
+        {
+            // All load instructions are pushing instructions.
+            deleteWithSubsequentPop(codeAttrInfo, offset, variableInstruction);
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Deletes the given instruction and its subsequent compatible pop instruction,
+     * if any, and if the latter is not a branch target.
+     */
+    private void deleteWithSubsequentPop(CodeAttrInfo codeAttrInfo,
+                                         int          offset,
+                                         Instruction  instruction)
+    {
+        boolean isCategory2 = instruction.isCategory2();
+
+        int nextOffset = offset + instruction.length(offset);
+
+        if (!codeAttrInfoEditor.isModified(offset)     &&
+            !codeAttrInfoEditor.isModified(nextOffset) &&
+            !branchTargetFinder.isBranchTarget(nextOffset))
+        {
+            Instruction nextInstruction = InstructionFactory.create(codeAttrInfo.code,
+                                                                    nextOffset);
+            int nextOpcode = nextInstruction.opcode;
+            if ((nextOpcode == InstructionConstants.OP_POP ||
+                 nextOpcode == InstructionConstants.OP_POP2) &&
+                nextInstruction.isCategory2() == isCategory2)
+            {
+                // Delete the pushing instruction and the pop instruction.
+                codeAttrInfoEditor.deleteInstruction(offset);
+                codeAttrInfoEditor.deleteInstruction(nextOffset);
+            }
+        }
+    }
+}
diff --git a/src/proguard/optimize/peephole/SingleImplementationInliner.java b/src/proguard/optimize/peephole/SingleImplementationInliner.java
new file mode 100644
index 0000000..c700199
--- /dev/null
+++ b/src/proguard/optimize/peephole/SingleImplementationInliner.java
@@ -0,0 +1,534 @@
+/* $Id: SingleImplementationInliner.java,v 1.7 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.editor.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This MemberInfoVisitor and AttrInfoVisitor replaces all references to
+ * interfaces that have single implementations by references to those
+ * implementations.
+ *
+ * @author Eric Lafortune
+ */
+public class SingleImplementationInliner
+implements   MemberInfoVisitor,
+             AttrInfoVisitor,
+             InstructionVisitor,
+             CpInfoVisitor,
+             LocalVariableInfoVisitor,
+             LocalVariableTypeInfoVisitor
+{
+    private MemberFinder memberFinder = new MemberFinder();
+
+    private ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
+    private CodeAttrInfoEditor codeAttrInfoEditor = new CodeAttrInfoEditor(1024);
+
+
+    // Return values of the single implementation inliner.
+    private int       cpIndex;
+    private ClassFile singleImplementationClassFile;
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        visitMemberInfo(programClassFile, programFieldInfo);
+    }
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        visitMemberInfo(programClassFile, programMethodInfo);
+    }
+
+    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    {
+        // Update the member info if any of its referenced classes
+        // is an interface with a single implementation.
+        ClassFile[] referencedClassFiles =
+            updateReferencedClassFiles(programMemberInfo.referencedClassFiles);
+
+        // Update the descriptor if necessary.
+        if (referencedClassFiles != null)
+        {
+            programMemberInfo.u2descriptorIndex =
+                constantPoolEditor.addUtf8CpInfo(programClassFile,
+                                                 newDescriptor(programMemberInfo.getDescriptor(programClassFile),
+                                                               referencedClassFiles));
+
+            programMemberInfo.referencedClassFiles = referencedClassFiles;
+        }
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
+
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        // Reset the code changes.
+        codeAttrInfoEditor.reset(codeAttrInfo.u4codeLength);
+
+        // Update the instructions that refer to interfaces with a single
+        // implementation.
+        codeAttrInfo.instructionsAccept(classFile, methodInfo, this);
+
+        // Apply the code changes.
+        codeAttrInfoEditor.visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
+
+        //codeAttrInfo.attributesAccept(classFile, methodInfo, this);
+    }
+
+
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
+    {
+        // Rename the types of the local variables.
+        localVariableTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+    }
+
+
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
+    {
+        // Rename the signatures of the local variables.
+        localVariableTypeTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+    }
+
+
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
+    {
+        // TODO: Update signature attribute.
+    }
+
+
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
+    {
+        // TODO: Update runtime visible annotation attribute.
+
+        //runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
+    {
+        // TODO: Update runtime invisible annotation attribute.
+
+        //runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
+    {
+        // TODO: Update runtime visible parameter annotation attribute.
+
+        //runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
+    {
+        // TODO: Update runtime invisible parameter annotation attribute.
+
+        //runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
+    {
+        // TODO: Update annotation default attribute.
+
+        //annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+
+
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    {
+        cpIndex = 0;
+        classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
+
+        if (cpIndex != 0)
+        {
+            byte opcode = cpInstruction.opcode;
+
+            // Does this instruction now invoke a single implementation?
+            if (opcode == InstructionConstants.OP_INVOKEINTERFACE &&
+                singleImplementationClassFile != null)
+            {
+                // Replace the interface invocation by an ordinary invocation.
+                opcode = InstructionConstants.OP_INVOKEVIRTUAL;
+            }
+
+            // Replace the instruction by an updated instruction.
+            Instruction replacementInstruction = new CpInstruction(opcode,
+                                                                   cpIndex,
+                                                                   cpInstruction.constant).shrink();
+
+            codeAttrInfoEditor.replaceInstruction(offset, replacementInstruction);
+        }
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+
+
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
+    {
+        // Create a new string entry if its referenced class is an interface with
+        // a single implementation.
+        singleImplementationClassFile =
+            SingleImplementationMarker.singleImplementation(stringCpInfo.referencedClassFile);
+
+        if (singleImplementationClassFile != null)
+        {
+            // Create a new string entry.
+            cpIndex = constantPoolEditor.addStringCpInfo((ProgramClassFile)classFile,
+                                                         singleImplementationClassFile.getName(),
+                                                         singleImplementationClassFile);
+        }
+    }
+
+
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    {
+        // Create a new field reference entry if its type has changed.
+        cpIndex = 0;
+        classFile.constantPoolEntryAccept(fieldrefCpInfo.getNameAndTypeIndex(), this);
+        int nameAndTypeIndex = cpIndex;
+
+        if (nameAndTypeIndex != 0)
+        {
+            // Create a new field reference entry.
+            cpIndex = constantPoolEditor.addFieldrefCpInfo((ProgramClassFile)classFile,
+                                                           fieldrefCpInfo.getClassIndex(),
+                                                           nameAndTypeIndex,
+                                                           fieldrefCpInfo.referencedClassFile,
+                                                           fieldrefCpInfo.referencedMemberInfo);
+        }
+    }
+
+
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    {
+        // Create a new method reference entry if its type has changed.
+        cpIndex = 0;
+        classFile.constantPoolEntryAccept(methodrefCpInfo.getNameAndTypeIndex(), this);
+        int nameAndTypeIndex = cpIndex;
+
+        if (nameAndTypeIndex != 0)
+        {
+            // Create a new method reference entry.
+            cpIndex = constantPoolEditor.addMethodrefCpInfo((ProgramClassFile)classFile,
+                                                            methodrefCpInfo.getClassIndex(),
+                                                            nameAndTypeIndex,
+                                                            methodrefCpInfo.referencedClassFile,
+                                                            methodrefCpInfo.referencedMemberInfo);
+        }
+    }
+
+
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    {
+        // Create a new ordinary method reference entry, if its referenced class
+        // is an interface with a single implementation, or a a new interface
+        // method reference entry, if its type has changed.
+        cpIndex = 0;
+        classFile.constantPoolEntryAccept(interfaceMethodrefCpInfo.getClassIndex(), this);
+        int classIndex = cpIndex;
+
+        cpIndex = 0;
+        classFile.constantPoolEntryAccept(interfaceMethodrefCpInfo.getNameAndTypeIndex(), this);
+        int nameAndTypeIndex = cpIndex;
+
+        if (classIndex != 0)
+        {
+            if (nameAndTypeIndex == 0)
+            {
+                nameAndTypeIndex = interfaceMethodrefCpInfo.getNameAndTypeIndex();
+            }
+
+            // See if we can find the referenced method.
+            ClassFile referencedClassFile = singleImplementationClassFile;
+
+            String name = interfaceMethodrefCpInfo.getName(classFile);
+            String type = interfaceMethodrefCpInfo.getType(classFile);
+
+            // See if we can find the referenced class membver somewhere in the
+            // hierarchy.
+            MethodInfo referencedMethodInfo = memberFinder.findMethod(referencedClassFile,
+                                                                      name,
+                                                                      type);
+            referencedClassFile             = memberFinder.correspondingClassFile();
+
+            // Create an ordinary method reference entry.
+            cpIndex = constantPoolEditor.addMethodrefCpInfo((ProgramClassFile)classFile,
+                                                            classIndex,
+                                                            nameAndTypeIndex,
+                                                            referencedClassFile,
+                                                            referencedMethodInfo);
+        }
+        else if (nameAndTypeIndex != 0)
+        {
+            classIndex = interfaceMethodrefCpInfo.getClassIndex();
+
+            // Create an interface method reference entry.
+            cpIndex = constantPoolEditor.addInterfaceMethodrefCpInfo((ProgramClassFile)classFile,
+                                                                     classIndex,
+                                                                     nameAndTypeIndex,
+                                                                     interfaceMethodrefCpInfo.referencedClassFile,
+                                                                     interfaceMethodrefCpInfo.referencedMemberInfo);
+        }
+    }
+
+
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    {
+        // Create a new class entry if its referenced class is an interface with
+        // a single implementation.
+        singleImplementationClassFile =
+            SingleImplementationMarker.singleImplementation(classCpInfo.referencedClassFile);
+
+        if (singleImplementationClassFile != null)
+        {
+            // Create a new class entry.
+            cpIndex = constantPoolEditor.addClassCpInfo((ProgramClassFile)classFile,
+                                                        newClassName(classCpInfo.getName(classFile),
+                                                                     singleImplementationClassFile),
+                                                        singleImplementationClassFile);
+        }
+    }
+
+
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo)
+    {
+        // Create a new name and type entry if any of the referenced classes of
+        // its type is an interface with a single implementation.
+        ClassFile[] referencedClassFiles =
+            updateReferencedClassFiles(nameAndTypeCpInfo.referencedClassFiles);
+
+        if (referencedClassFiles != null)
+        {
+            // Create a new name and type entry.
+            cpIndex = constantPoolEditor.addNameAndTypeCpInfo((ProgramClassFile)classFile,
+                                                              nameAndTypeCpInfo.getName(classFile),
+                                                              newDescriptor(nameAndTypeCpInfo.getType(classFile),
+                                                                            referencedClassFiles),
+                                                              referencedClassFiles);
+        }
+    }
+
+
+    // Implementations for LocalVariableInfoVisitor.
+
+    public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo)
+    {
+        // Create a new type entry if its referenced class is an interface with
+        // a single implementation.
+        ClassFile singleImplementationClassFile =
+            SingleImplementationMarker.singleImplementation(localVariableInfo.referencedClassFile);
+
+        // Update the type if necessary.
+        if (singleImplementationClassFile != null)
+        {
+            // Refer to a new Utf8 entry.
+            localVariableInfo.u2descriptorIndex =
+                constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
+                                                 newClassName(classFile.getCpString(localVariableInfo.u2descriptorIndex),
+                                                              singleImplementationClassFile));
+        }
+    }
+
+
+    // Implementations for LocalVariableTypeInfoVisitor.
+
+    public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo)
+    {
+        // Create a new signature entry if any of the referenced classes of
+        // its type is an interface with a single implementation.
+        ClassFile[] referencedClassFiles =
+            updateReferencedClassFiles(localVariableTypeInfo.referencedClassFiles);
+
+        // Update the signature if necessary.
+        if (referencedClassFiles != null)
+        {
+            localVariableTypeInfo.u2signatureIndex =
+                constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
+                                                 newDescriptor(classFile.getCpString(localVariableTypeInfo.u2signatureIndex),
+                                                               referencedClassFiles));
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Updates the given array of referenced class files, if the refer to an
+     * interface with a single implementation. Returns a new array if it
+     * needed to be updated, or <code>null</code> otherwise.
+     */
+    private ClassFile[] updateReferencedClassFiles(ClassFile[] referencedClassFiles)
+    {
+        ClassFile[] newReferencedClassFiles = null;
+
+        // Check all referenced classes.
+        if (referencedClassFiles != null &&
+            referencedClassFilesChanged(referencedClassFiles))
+        {
+            // Create a new array to copy the elements.
+            newReferencedClassFiles = new ClassFile[referencedClassFiles.length];
+
+            // Update all referenced classes.
+            for (int index = 0; index < referencedClassFiles.length; index++)
+            {
+                ClassFile referencedClassFile = referencedClassFiles[index];
+
+                // See if we have is an interface with a single implementation.
+                ClassFile singleImplementationClassFile =
+                    SingleImplementationMarker.singleImplementation(referencedClassFile);
+
+                // Update or copy the referenced class file.
+                newReferencedClassFiles[index] = singleImplementationClassFile != null ?
+                    singleImplementationClassFile :
+                    referencedClassFile;
+            }
+        }
+
+        return newReferencedClassFiles;
+    }
+
+
+    private boolean referencedClassFilesChanged(ClassFile[] referencedClassFiles)
+    {
+        // Check all referenced classes.
+        for (int index = 0; index < referencedClassFiles.length; index++)
+        {
+            // See if we have is an interface with a single implementation.
+            if (SingleImplementationMarker.singleImplementation(referencedClassFiles[index]) != null)
+            {
+                // We've found an element that needs to be updated.
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Returns the new descriptor based on the given descriptor and the new
+     * names of the given referenced class files.
+     */
+    private String newDescriptor(String      descriptor,
+                                 ClassFile[] referencedClassFiles)
+    {
+        // Unravel and reconstruct the class elements of the descriptor.
+        DescriptorClassEnumeration descriptorClassEnumeration =
+            new DescriptorClassEnumeration(descriptor);
+
+        String newDescriptor = descriptorClassEnumeration.nextFluff();
+
+        int index = 0;
+        while (descriptorClassEnumeration.hasMoreClassNames())
+        {
+            String className = descriptorClassEnumeration.nextClassName();
+            String fluff     = descriptorClassEnumeration.nextFluff();
+
+            String newClassName = newClassName(className,
+                                               referencedClassFiles[index++]);
+
+            // Fall back on the original class name if there is no new name.
+            if (newClassName == null)
+            {
+                newClassName = className;
+            }
+
+            newDescriptor = newDescriptor + newClassName + fluff;
+        }
+
+        return newDescriptor;
+    }
+
+
+    /**
+     * Returns the new class name based on the given class name and the new
+     * name of the given referenced class file. Class names of array types
+     * are handled properly.
+     */
+    private String newClassName(String    className,
+                                ClassFile referencedClassFile)
+    {
+        // If there is no new class name, the descriptor doesn't change.
+        if (referencedClassFile == null)
+        {
+            return className;
+        }
+
+        // Reconstruct the class name.
+        String newClassName = referencedClassFile.getName();
+
+        // Is it an array type?
+        if (className.charAt(0) == ClassConstants.INTERNAL_TYPE_ARRAY)
+        {
+            // Add the array prefixes and suffix "[L...;".
+            newClassName =
+                 className.substring(0, className.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_START)+1) +
+                 newClassName +
+                 ClassConstants.INTERNAL_TYPE_CLASS_END;
+        }
+
+        return newClassName;
+    }
+}
diff --git a/src/proguard/optimize/peephole/SingleImplementationMarker.java b/src/proguard/optimize/peephole/SingleImplementationMarker.java
new file mode 100644
index 0000000..472fe4c
--- /dev/null
+++ b/src/proguard/optimize/peephole/SingleImplementationMarker.java
@@ -0,0 +1,160 @@
+/* $Id: SingleImplementationMarker.java,v 1.3 2004/11/20 15:41:24 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.*;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+import proguard.optimize.*;
+
+/**
+ * This ClassFileVisitor investigates all class files that it visits to see
+ * whether they have/are the sole (non-abstract) implementation of an interface.
+ * It may already modify the access of the single implementing class to match
+ * the access of the interface.
+ *
+ * @author Eric Lafortune
+ */
+public class SingleImplementationMarker
+implements   ClassFileVisitor
+{
+    // A visitor info flag to indicate that a ClassFile object is the single
+    // implementation of an interface.
+    private static final Object SINGLE_IMPLEMENTATION = new Object();
+
+
+    private boolean allowAccessModification;
+
+
+    /**
+     * Creates a new SingleImplementationMarker.
+     * @param allowAccessModification indicates whether the access modifiers of
+     *                                a class can be changed in order to inline
+     *                                it.
+     */
+    public SingleImplementationMarker(boolean allowAccessModification)
+    {
+        this.allowAccessModification = allowAccessModification;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // The program class must be an interface class that cannot be
+        // implemented again.
+        if ((programClassFile.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) == 0 ||
+            KeepMarker.isKept(programClassFile))
+        {
+            return;
+        }
+
+        // The interface class must have a single implementation.
+        ClassFile[] subClasses = programClassFile.subClasses;
+        if (subClasses == null ||
+            subClasses.length != 1)
+        {
+            return;
+        }
+
+        // If the single implementation is an interface, check it recursively.
+        ClassFile singleImplementationClassFile = subClasses[0];
+        int singleImplementationAccessFlags = singleImplementationClassFile.getAccessFlags();
+        if ((singleImplementationAccessFlags & ClassConstants.INTERNAL_ACC_INTERFACE) != 0)
+        {
+            singleImplementationClassFile.accept(this);
+
+            // See if the subinterface has a single implementation.
+            singleImplementationClassFile = singleImplementation(singleImplementationClassFile);
+            if (singleImplementationClassFile == null)
+            {
+                return;
+            }
+
+            singleImplementationAccessFlags = singleImplementationClassFile.getAccessFlags();
+        }
+
+        // The single implementation must not be abstract.
+        else if ((singleImplementationAccessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0)
+        {
+            return;
+        }
+
+        // Doesn't the implementation have at least the same access as the
+        // interface?
+        if (AccessUtil.accessLevel(singleImplementationAccessFlags) <
+            AccessUtil.accessLevel(programClassFile.getAccessFlags()))
+        {
+            // Are we allowed to fix the access?
+            if (allowAccessModification)
+            {
+                // Fix the access.
+                ((ProgramClassFile)singleImplementationClassFile).u2accessFlags =
+                    AccessUtil.replaceAccessFlags(singleImplementationAccessFlags,
+                                                  programClassFile.getAccessFlags());
+            }
+            else
+            {
+                // We can't give the implementation the access of the interface.
+                // Forget about inlining it after all.
+                return;
+            }
+        }
+
+        // Mark the interface and its single implementation.
+        markSingleImplementation(programClassFile, singleImplementationClassFile);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile) {}
+
+
+    // Small utility methods.
+
+    public static void markSingleImplementation(VisitorAccepter visitorAccepter,
+                                                ClassFile       singleImplementation)
+    {
+        //System.out.println("Marking single implementation ["+((ClassFile)visitorAccepter).getName()+"] -> ["+singleImplementation.getName()+"]");
+
+        // The interface has a single implementation.
+        visitorAccepter.setVisitorInfo(singleImplementation);
+
+        // The class is a single implementation.
+        singleImplementation.setVisitorInfo(SINGLE_IMPLEMENTATION);
+    }
+
+
+    public static ClassFile singleImplementation(VisitorAccepter visitorAccepter)
+    {
+        return visitorAccepter != null &&
+               visitorAccepter.getVisitorInfo() instanceof ClassFile ?
+                   (ClassFile)visitorAccepter.getVisitorInfo() :
+                   null;
+    }
+
+
+    public static boolean isSingleImplementation(VisitorAccepter visitorAccepter)
+    {
+        return visitorAccepter != null &&
+               visitorAccepter.getVisitorInfo() == SINGLE_IMPLEMENTATION;
+    }
+}
diff --git a/src/proguard/optimize/peephole/StoreLoadReplacer.java b/src/proguard/optimize/peephole/StoreLoadReplacer.java
new file mode 100644
index 0000000..74e62f9
--- /dev/null
+++ b/src/proguard/optimize/peephole/StoreLoadReplacer.java
@@ -0,0 +1,116 @@
+/* $Id: StoreLoadReplacer.java,v 1.7 2004/11/20 15:06:55 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.editor.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This InstructionVisitor replaces store/load instruction pairs by equivalent
+ * dup/store instruction pairs.
+ *
+ * @author Eric Lafortune
+ */
+public class StoreLoadReplacer implements InstructionVisitor
+{
+    // Some Instruction objects that can be reused.
+    private Instruction dupInstruction  = new SimpleInstruction(InstructionConstants.OP_DUP);
+    private Instruction dup2Instruction = new SimpleInstruction(InstructionConstants.OP_DUP2);
+
+    private BranchTargetFinder branchTargetFinder;
+    private CodeAttrInfoEditor codeAttrInfoEditor;
+
+
+    /**
+     * Creates a new StoreLoadReplacer.
+     * @param branchTargetFinder a branch target finder that has been
+     *                           initialized to indicate branch targets
+     *                           in the visited code.
+     * @param codeAttrInfoEditor a code editor that can be used for
+     *                           accumulating changes to the code.
+     */
+    public StoreLoadReplacer(BranchTargetFinder branchTargetFinder,
+                             CodeAttrInfoEditor codeAttrInfoEditor)
+    {
+        this.branchTargetFinder = branchTargetFinder;
+        this.codeAttrInfoEditor = codeAttrInfoEditor;
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction) {}
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+
+
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    {
+        // Is this instruction a regular store instruction?
+        if (!variableInstruction.isLoad() &&
+            variableInstruction.opcode != InstructionConstants.OP_IINC)
+        {
+            byte opcode        = variableInstruction.opcode;
+            int  variableIndex = variableInstruction.variableIndex;
+
+            int nextOffset = offset + variableInstruction.length(offset);
+
+            if (!codeAttrInfoEditor.isModified(offset)     &&
+                !codeAttrInfoEditor.isModified(nextOffset) &&
+                !branchTargetFinder.isBranchTarget(nextOffset))
+            {
+                // Is the next instruction a corresponding load instruction?
+                Instruction nextInstruction = InstructionFactory.create(codeAttrInfo.code,
+                                                                        nextOffset);
+
+                if (nextInstruction instanceof VariableInstruction)
+                {
+                    variableInstruction = (VariableInstruction)nextInstruction;
+                    if (variableInstruction.isLoad()                              &&
+                        variableInstruction.opcode != InstructionConstants.OP_RET &&
+                        variableInstruction.variableIndex == variableIndex)
+                    {
+                        // Replace the store instruction by a matching dup instruction.
+                        Instruction matchingDupInstruction = variableInstruction.isCategory2() ?
+                            dup2Instruction :
+                            dupInstruction;
+
+                        codeAttrInfoEditor.replaceInstruction(offset,
+                                                              matchingDupInstruction);
+
+                        // Replace the load instruction by the store instruction.
+                        Instruction storeInstruction =
+                             new VariableInstruction(opcode,
+                                                     variableInstruction.variableIndex).shrink();
+
+                        codeAttrInfoEditor.replaceInstruction(nextOffset,
+                                                              storeInstruction);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/proguard/optimize/peephole/package.html b/src/proguard/optimize/peephole/package.html
new file mode 100644
index 0000000..e0eeb51
--- /dev/null
+++ b/src/proguard/optimize/peephole/package.html
@@ -0,0 +1,3 @@
+<body>
+This package contains visitors that perform various peephole optimizations.
+</body>
diff --git a/src/proguard/package.html b/src/proguard/package.html
new file mode 100644
index 0000000..986cad8
--- /dev/null
+++ b/src/proguard/package.html
@@ -0,0 +1,5 @@
+<body>
+This package contains the main ProGuard application.
+ProGuard can read jar files, shrink and obfuscate them, and write out the
+resulting jar file.
+</body>
diff --git a/src/proguard/retrace/ReTrace.java b/src/proguard/retrace/ReTrace.java
new file mode 100644
index 0000000..b981572
--- /dev/null
+++ b/src/proguard/retrace/ReTrace.java
@@ -0,0 +1,155 @@
+/* $Id: ReTrace.java,v 1.7 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.retrace;
+
+import java.io.IOException;
+
+import proguard.obfuscate.MappingReader;
+
+
+/**
+ * Tool for de-obfuscating stack traces of applications that were obfuscated
+ * with ProGuard.
+ *
+ * @author Eric Lafortune
+ */
+public class ReTrace
+{
+    private static final String VERBOSE_OPTION = "-verbose";
+
+
+    // The class settings.
+    private boolean verbose;
+    private String  mappingFileName;
+    private String  stackTraceFileName;
+
+
+    /**
+     * Creates a new ReTrace object to process stack traces on the standard
+     * input, based on the given mapping file name.
+     * @param verbose         specifies whether the de-obfuscated stack trace
+     *                        should be verbose.
+     * @param mappingFileName the mapping file that was written out by ProGuard.
+     */
+    public ReTrace(boolean verbose,
+                   String  mappingFileName)
+    {
+        this(verbose, mappingFileName, null);
+    }
+
+
+    /**
+     * Creates a new ReTrace object to process a stack trace from the given file,
+     * based on the given mapping file name.
+     * @param verbose            specifies whether the de-obfuscated stack trace
+     *                           should be verbose.
+     * @param mappingFileName    the mapping file that was written out by
+     *                           ProGuard.
+     * @param stackTraceFileName the name of the file that contains the stack
+     *                           trace.
+     */
+    public ReTrace(boolean verbose,
+                   String  mappingFileName,
+                   String  stackTraceFileName)
+    {
+        this.verbose            = verbose;
+        this.mappingFileName    = mappingFileName;
+        this.stackTraceFileName = stackTraceFileName;
+    }
+
+
+    /**
+     * Performs the subsequent ReTrace operations.
+     */
+    public void execute() throws IOException
+    {
+        StackTrace stackTrace = new StackTrace(verbose);
+        MappingReader reader = new MappingReader(mappingFileName);
+
+        // Read the obfuscated stack trace.
+        stackTrace.read(stackTraceFileName);
+
+        // Resolve the obfuscated stack trace by means of the mapping file.
+        reader.pump(stackTrace);
+
+        // Print out the resolved stack trace.
+        stackTrace.print();
+    }
+
+
+    /**
+     * The main program for ReTrace.
+     */
+    public static void main(String[] args)
+    {
+        if (args.length < 1)
+        {
+            System.err.println("Usage: java proguard.ReTrace [-verbose] <mapping_file> [<stacktrace_file>]");
+            System.exit(-1);
+        }
+
+        int argumentIndex = 0;
+
+        boolean verbose = false;
+        if (args[argumentIndex].equals(VERBOSE_OPTION))
+        {
+            verbose = true;
+            argumentIndex++;
+
+            if (args.length < 2)
+            {
+                System.err.println("Usage: java proguard.ReTrace [-verbose] <mapping_file> [<stacktrace_file>]");
+                System.exit(-1);
+            }
+        }
+
+        String mappingFileName    = args[argumentIndex++];
+        String stackTraceFileName = argumentIndex < args.length ?
+            args[argumentIndex++] :
+            null;
+
+        ReTrace reTrace = new ReTrace(verbose,
+                                      mappingFileName,
+                                      stackTraceFileName);
+        try
+        {
+            // Execute ReTrace with its given settings.
+            reTrace.execute();
+        }
+        catch (IOException ex)
+        {
+            if (verbose)
+            {
+                // Print a verbose stack trace.
+                ex.printStackTrace();
+            }
+            else
+            {
+                // Print just the stack trace message.
+                System.err.println("Error: "+ex.getMessage());
+            }
+
+            System.exit(1);
+        }
+
+        System.exit(0);
+    }
+}
diff --git a/src/proguard/retrace/StackTrace.java b/src/proguard/retrace/StackTrace.java
new file mode 100644
index 0000000..50961c9
--- /dev/null
+++ b/src/proguard/retrace/StackTrace.java
@@ -0,0 +1,177 @@
+/* $Id: StackTrace.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.retrace;
+
+import java.io.*;
+import java.util.*;
+
+import proguard.obfuscate.MappingProcessor;
+
+
+/**
+ * This class represents an obfuscated stack trace. It can read, de-obfuscate,
+ * and then write its contents.
+ *
+ * @author Eric Lafortune
+ */
+class StackTrace implements MappingProcessor
+{
+    // The stack trace settings.
+    private boolean verbose;
+
+    // The stack trace items.
+    private List stackTraceItems = new ArrayList();
+
+
+    /**
+     * Creates a new StackTrace.
+     * @param verbose specifies whether the de-obfuscated stack trace should
+     *                be verbose.
+     */
+    public StackTrace(boolean verbose)
+    {
+        this.verbose = verbose;
+    }
+
+
+    /**
+     * Reads the stack trace file.
+     */
+    public void read(String stackTraceFileName) throws IOException
+    {
+        LineNumberReader lineNumberReader = null;
+
+        try
+        {
+            Reader reader = stackTraceFileName == null ?
+                (Reader)new InputStreamReader(System.in) :
+                (Reader)new BufferedReader(new FileReader(stackTraceFileName));
+
+            lineNumberReader = new LineNumberReader(reader);
+
+            // Read the line in the stack trace.
+            while (true)
+            {
+                String line = lineNumberReader.readLine();
+                if (line == null)
+                {
+                    break;
+                }
+
+                line = line.trim();
+                if (line.length() == 0)
+                {
+                    continue;
+                }
+
+                // Put the line in a stack trace item.
+                StackTraceItem item = new StackTraceItem(verbose);
+
+                item.parse(line);
+
+                stackTraceItems.add(item);
+            }
+        }
+        catch (IOException ex)
+        {
+            throw new IOException("Can't read stack trace (" + ex.getMessage() + ")");
+        }
+        finally
+        {
+            if (stackTraceFileName != null &&
+                lineNumberReader != null)
+            {
+                try
+                {
+                    lineNumberReader.close();
+                }
+                catch (IOException ex)
+                {
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Prints out the de-obfuscated stack trace.
+     */
+    public void print()
+    {
+        // Delegate to each of the stack trace items.
+        for (int index = 0; index < stackTraceItems.size(); index++)
+        {
+            StackTraceItem item = (StackTraceItem)stackTraceItems.get(index);
+
+            item.print();
+        }
+    }
+
+
+    // Implementations for MappingProcessor.
+
+    public boolean processClassFileMapping(String className,
+                                           String newClassName)
+    {
+        // Delegate to each of the stack trace items.
+        boolean present = false;
+        for (int index = 0; index < stackTraceItems.size(); index++)
+        {
+            StackTraceItem item = (StackTraceItem)stackTraceItems.get(index);
+
+            present |= item.processClassFileMapping(className,
+                                                    newClassName);
+        }
+
+        return present;
+    }
+
+
+    public void processFieldMapping(String className,
+                                    String fieldType,
+                                    String fieldName,
+                                    String newFieldName)
+    {
+        // A stack trace never contains any fields.
+    }
+
+
+    public void processMethodMapping(String className,
+                                     int    firstLineNumber,
+                                     int    lastLineNumber,
+                                     String methodReturnType,
+                                     String methodNameAndArguments,
+                                     String newMethodName)
+    {
+        // Delegate to each of the stack trace items.
+        for (int index = 0; index < stackTraceItems.size(); index++)
+        {
+            StackTraceItem item = (StackTraceItem)stackTraceItems.get(index);
+
+            item.processMethodMapping(className,
+                                      firstLineNumber,
+                                      lastLineNumber,
+                                      methodReturnType,
+                                      methodNameAndArguments,
+                                      newMethodName);
+        }
+    }
+}
diff --git a/src/proguard/retrace/StackTraceItem.java b/src/proguard/retrace/StackTraceItem.java
new file mode 100644
index 0000000..f3e30c3
--- /dev/null
+++ b/src/proguard/retrace/StackTraceItem.java
@@ -0,0 +1,294 @@
+/* $Id: StackTraceItem.java,v 1.7 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.retrace;
+
+import java.io.*;
+import java.util.*;
+
+import proguard.obfuscate.MappingProcessor;
+
+
+/**
+ * This class represents an obfuscated stack trace item. It can read, de-obfuscate,
+ * and then write its contents.
+ *
+ * @author Eric Lafortune
+ */
+class StackTraceItem implements MappingProcessor
+{
+    // The stack trace settings.
+    private boolean verbose;
+
+    public String prefix;
+    public String obfuscatedClassName;
+    public String obfuscatedMethodName;
+    public String sourceFile;
+    public int    lineNumber;
+    public String suffix;
+
+    public String originalClassName;
+    public List   originalMethodNames;
+
+    /**
+     * Creates a new StackTraceItem.
+     * @param verbose specifies whether the de-obfuscated stack trace should
+     *                be verbose.
+     */
+    public StackTraceItem(boolean verbose)
+    {
+        this.verbose = verbose;
+    }
+
+
+    /**
+     * Parses the stack trace
+     */
+    public void parse(String line) throws IOException
+    {
+        if (!parseAtLine(line) &&
+            !parseExceptionInThreadLine(line))
+        {
+            parseAnyLine(line);
+        }
+    }
+
+
+    /**
+     * Tries to parse "at ___.___(___:___)", containing the class name,
+     * the method name, the source file, and the optional line number.
+     */
+    private boolean parseAtLine(String line)
+    {
+        if (!line.startsWith("at "))
+        {
+            return false;
+        }
+
+        int openParenthesisIndex = line.indexOf('(', 3);
+        if (openParenthesisIndex < 0)
+        {
+            return false;
+        }
+
+        int colonIndex = line.indexOf(':', openParenthesisIndex + 1);
+
+        int closeParenthesisIndex = line.indexOf(')', Math.max(openParenthesisIndex, colonIndex) + 1);
+        if (closeParenthesisIndex < 0)
+        {
+            return false;
+        }
+
+        int periodIndex = line.lastIndexOf('.', openParenthesisIndex - 1);
+        if (periodIndex < 0)
+        {
+            return false;
+        }
+
+        prefix               = "        at ";
+        obfuscatedClassName  = line.substring(3, periodIndex).trim();
+        obfuscatedMethodName = line.substring(periodIndex + 1, openParenthesisIndex).trim();
+        sourceFile           = line.substring(openParenthesisIndex + 1, colonIndex < 0 ? closeParenthesisIndex : colonIndex).trim();
+        lineNumber           = colonIndex < 0 ? 0 : Integer.parseInt(line.substring(colonIndex + 1, closeParenthesisIndex).trim());
+
+        return true;
+    }
+
+
+    /**
+     * Tries to parse "Exception in thread "___" ___:___" or just "___:___",
+     * containing the optional thread name, the exception class name and the
+     * exception message.
+     */
+    private boolean parseExceptionInThreadLine(String line)
+    {
+        // Trim away the thread message part, if any.
+        if (line.startsWith("Exception in thread \""))
+        {
+            int quote_index = line.indexOf('"', 21);
+            if (quote_index < 0)
+            {
+                return false;
+            }
+
+            prefix = line.substring(0, quote_index+1) + " ";
+            line   = line.substring(quote_index+1).trim();
+        }
+
+        int colonIndex = line.indexOf(':');
+        if (colonIndex < 0)
+        {
+            return false;
+        }
+
+        int spaceIndex = line.lastIndexOf(' ', colonIndex);
+
+        prefix              = line.substring(0, spaceIndex+1);
+        obfuscatedClassName = line.substring(spaceIndex+1, colonIndex).trim();;
+        suffix              = line.substring(colonIndex);
+
+        return true;
+    }
+
+
+    /**
+     * Parses any line.
+     */
+    private void parseAnyLine(String line)
+    {
+        prefix = line;
+    }
+
+
+    /**
+     * Prints out the de-obfuscated stack trace.
+     */
+    public void print()
+    {
+        // Get the original class name, if we found it.
+        String className = originalClassName != null ?
+            originalClassName :
+            obfuscatedClassName;
+
+        // Get the first original method name, if we found it.
+        String methodName = originalMethodNames != null ?
+            (String)originalMethodNames.get(0) :
+            obfuscatedMethodName;
+
+        // Compose the source file with the line number, if any.
+        String source = lineNumber != 0 ?
+            sourceFile + ":" + lineNumber :
+            sourceFile;
+
+        // Print out the resolved stack trace
+        if (prefix != null)
+        {
+            System.out.print(prefix);
+        }
+
+        if (className != null)
+        {
+            System.out.print(className);
+        }
+
+        if (methodName != null)
+        {
+            System.out.print("." + methodName + "(" + source + ")");
+
+            // Print out alternatives, if any.
+            if (originalMethodNames != null)
+            {
+                for (int otherMethodNameIndex = 1; otherMethodNameIndex < originalMethodNames.size(); otherMethodNameIndex++) {
+                    String otherMethodName = (String)originalMethodNames.get(otherMethodNameIndex);
+                    System.out.println();
+                    printSpaces(className.length()+12);
+                    System.out.print(otherMethodName);
+                }
+            }
+        }
+
+        if (suffix != null)
+        {
+            System.out.print(suffix);
+        }
+
+        System.out.println();
+    }
+
+
+    /**
+     * Prints the given number of spaces.
+     */
+    private void printSpaces(int aCount)
+    {
+        for (int counter = 0; counter < aCount; counter++)
+          System.out.print(' ');
+    }
+
+
+    // Implementations for MappingProcessor.
+
+    public boolean processClassFileMapping(String className,
+                                           String newClassName)
+    {
+        boolean present = false;
+
+        if (newClassName.equals(obfuscatedClassName))
+        {
+            originalClassName = className;
+            present = true;
+        }
+
+        return present;
+    }
+
+
+    public void processFieldMapping(String className,
+                                    String fieldType,
+                                    String fieldName,
+                                    String newFieldName)
+    {
+        // A stack trace item never contains any fields.
+    }
+
+
+    public void processMethodMapping(String className,
+                                     int    firstLineNumber,
+                                     int    lastLineNumber,
+                                     String methodReturnType,
+                                     String methodNameAndArguments,
+                                     String newMethodName)
+    {
+        if (className.equals(originalClassName) &&
+            newMethodName.equals(obfuscatedMethodName) &&
+            (lineNumber == 0 ||
+             firstLineNumber == 0 ||
+             lastLineNumber  == 0 ||
+             (firstLineNumber <= lineNumber &&
+              lastLineNumber  >= lineNumber)))
+        {
+            // Create a List for storing solutions for this
+            // method name.
+            if (originalMethodNames == null)
+            {
+                originalMethodNames = new ArrayList();
+            }
+
+            // Does the method have line numbers?
+            if (firstLineNumber != 0 &&
+                lastLineNumber  != 0 &&
+                lineNumber      != 0)
+            {
+                // Then it will be the one and only solution.
+                obfuscatedMethodName = null;
+                originalMethodNames.clear();
+            }
+
+            // Include return type and arguments in the method name if
+            // we're in verbose mode, otherwise strip the arguments.
+            String originalMethodName = verbose ?
+                (methodReturnType + " " + methodNameAndArguments) :
+                methodNameAndArguments.substring(0, methodNameAndArguments.indexOf('('));
+
+            // Add this method name solution to the list.
+            originalMethodNames.add(originalMethodName);
+        }
+    }
+}
diff --git a/src/proguard/retrace/package.html b/src/proguard/retrace/package.html
new file mode 100644
index 0000000..a35ce21
--- /dev/null
+++ b/src/proguard/retrace/package.html
@@ -0,0 +1,4 @@
+<body>
+This package contains the main ReTrace application.
+ReTrace can de-obfuscate stack traces of obfuscated programs.
+</body>
diff --git a/src/proguard/shrink/ClassFileShrinker.java b/src/proguard/shrink/ClassFileShrinker.java
new file mode 100644
index 0000000..5d5be32
--- /dev/null
+++ b/src/proguard/shrink/ClassFileShrinker.java
@@ -0,0 +1,341 @@
+/* $Id: ClassFileShrinker.java,v 1.25 2004/12/04 23:43:43 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.shrink;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.editor.ConstantPoolRemapper;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This ClassFileVisitor removes constant pool entries and class members that
+ * are not marked as being used.
+ *
+ * @see UsageMarker
+ *
+ * @author Eric Lafortune
+ */
+public class ClassFileShrinker
+  implements ClassFileVisitor,
+             MemberInfoVisitor,
+             AttrInfoVisitor
+{
+    private int[]                cpIndexMap;
+    private ConstantPoolRemapper constantPoolRemapper;
+
+
+    /**
+     * Creates a new ClassFileShrinker.
+     * @param codeLength an estimate of the maximum length of all the code that
+     *                   will be edited.
+     */
+    public ClassFileShrinker(int codeLength)
+    {
+        constantPoolRemapper = new ConstantPoolRemapper(codeLength);
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        // Shrink the arrays for constant pool, interfaces, fields, methods,
+        // and class attributes.
+        programClassFile.u2interfacesCount =
+            shrinkCpIndexArray(programClassFile.constantPool,
+                               programClassFile.u2interfaces,
+                               programClassFile.u2interfacesCount);
+
+        // Shrinking the constant pool also sets up an index map.
+        programClassFile.u2constantPoolCount =
+            shrinkConstantPool(programClassFile.constantPool,
+                               programClassFile.u2constantPoolCount);
+
+        programClassFile.u2fieldsCount =
+            shrinkArray(programClassFile.fields,
+                        programClassFile.u2fieldsCount);
+
+        programClassFile.u2methodsCount =
+            shrinkArray(programClassFile.methods,
+                        programClassFile.u2methodsCount);
+
+        programClassFile.u2attributesCount =
+            shrinkArray(programClassFile.attributes,
+                        programClassFile.u2attributesCount);
+
+        // Compact the remaining fields, methods, and attributes,
+        // and remap their references to the constant pool.
+        programClassFile.fieldsAccept(this);
+        programClassFile.methodsAccept(this);
+        programClassFile.attributesAccept(this);
+
+        // Remap all constant pool references.
+        constantPoolRemapper.setCpIndexMap(cpIndexMap);
+        constantPoolRemapper.visitProgramClassFile(programClassFile);
+
+        // Compact the extra field pointing to the subclasses of this class.
+        programClassFile.subClasses =
+            shrinkToNewArray(programClassFile.subClasses);
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        // Library class files are left unchanged.
+
+        // Compact the extra field pointing to the subclasses of this class.
+        libraryClassFile.subClasses =
+            shrinkToNewArray(libraryClassFile.subClasses);
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        visitMemberInfo(programClassFile, programFieldInfo);
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        visitMemberInfo(programClassFile, programMethodInfo);
+    }
+
+
+    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    {
+        // Compact the attributes array.
+        programMemberInfo.u2attributesCount =
+            shrinkArray(programMemberInfo.attributes,
+                        programMemberInfo.u2attributesCount);
+
+        // Compact any attributes.
+        programMemberInfo.attributesAccept(programClassFile, this);
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    {
+        // Library class files are left unchanged.
+    }
+
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        // Library class files are left unchanged.
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
+
+
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
+    {
+        // Compact the array of InnerClassesInfo objects.
+        innerClassesAttrInfo.u2numberOfClasses =
+            shrinkArray(innerClassesAttrInfo.classes,
+                        innerClassesAttrInfo.u2numberOfClasses);
+    }
+
+
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
+    {
+        // Sometimes, a class is still referenced (apparently as a dummy class),
+        // but its enclosing method is not. Then remove the reference to
+        // the enclosing method.
+        // E.g. the anonymous inner class javax.swing.JList$1 is defined inside
+        // a constructor of javax.swing.JList, but it is also referenced as a
+        // dummy argument in a constructor of javax.swing.JList$ListSelectionHandler.
+        if (enclosingMethodAttrInfo.referencedMethodInfo != null &&
+            !UsageMarker.isUsed(enclosingMethodAttrInfo.referencedMethodInfo))
+        {
+            enclosingMethodAttrInfo.u2nameAndTypeIndex = 0;
+
+            enclosingMethodAttrInfo.referencedMethodInfo = null;
+        }
+    }
+
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        // Compact the attributes array.
+        codeAttrInfo.u2attributesCount =
+            shrinkArray(codeAttrInfo.attributes,
+                        codeAttrInfo.u2attributesCount);
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Removes all entries that are not marked as being used from the given
+     * constant pool.
+     * @return the new number of entries.
+     */
+    private int shrinkConstantPool(CpInfo[] constantPool, int length)
+    {
+        if (cpIndexMap == null ||
+            cpIndexMap.length < length)
+        {
+            cpIndexMap = new int[length];
+        }
+
+        int     counter = 1;
+        boolean isUsed  = false;
+
+        // Shift the used constant pool entries together.
+        for (int index = 1; index < length; index++)
+        {
+            cpIndexMap[index] = counter;
+
+            CpInfo cpInfo = constantPool[index];
+
+            // Don't update the flag if this is the second half of a long entry.
+            if (cpInfo != null)
+            {
+                isUsed = UsageMarker.isUsed(cpInfo);
+            }
+
+            if (isUsed)
+            {
+                constantPool[counter++] = cpInfo;
+            }
+        }
+
+        // Clear the remaining constant pool elements.
+        for (int index = counter; index < length; index++)
+        {
+            constantPool[index] = null;
+        }
+
+        return counter;
+    }
+
+
+    /**
+     * Removes all indices that point to unused constant pool entries
+     * from the given array.
+     * @return the new number of indices.
+     */
+    private static int shrinkCpIndexArray(CpInfo[] constantPool, int[] array, int length)
+    {
+        int counter = 0;
+
+        // Shift the used objects together.
+        for (int index = 0; index < length; index++)
+        {
+            if (UsageMarker.isUsed(constantPool[array[index]]))
+            {
+                array[counter++] = array[index];
+            }
+        }
+
+        // Clear the remaining array elements.
+        for (int index = counter; index < length; index++)
+        {
+            array[index] = 0;
+        }
+
+        return counter;
+    }
+
+
+    /**
+     * Removes all ClassFile objects that are not marked as being used
+     * from the given array and returns the remaining objects in a an array
+     * of the right size.
+     * @return the new array.
+     */
+    private static ClassFile[] shrinkToNewArray(ClassFile[] array)
+    {
+        if (array == null)
+        {
+            return null;
+        }
+
+        // Shrink the given array in-place.
+        int length = shrinkArray(array, array.length);
+        if (length == 0)
+        {
+            return null;
+        }
+
+        // Return immediately if the array is of right size already.
+        if (length == array.length)
+        {
+            return array;
+        }
+
+        // Copy the remaining elements into a new array of the right size.
+        ClassFile[] newArray = new ClassFile[length];
+        System.arraycopy(array, 0, newArray, 0, length);
+        return newArray;
+    }
+
+
+    /**
+     * Removes all VisitorAccepter objects that are not marked as being used
+     * from the given array.
+     * @return the new number of VisitorAccepter objects.
+     */
+    private static int shrinkArray(VisitorAccepter[] array, int length)
+    {
+        int counter = 0;
+
+        // Shift the used objects together.
+        for (int index = 0; index < length; index++)
+        {
+            if (UsageMarker.isUsed(array[index]))
+            {
+                array[counter++] = array[index];
+            }
+        }
+
+        // Clear the remaining array elements.
+        for (int index = counter; index < length; index++)
+        {
+            array[index] = null;
+        }
+
+        return counter;
+    }
+}
diff --git a/src/proguard/shrink/InnerUsageMarker.java b/src/proguard/shrink/InnerUsageMarker.java
new file mode 100644
index 0000000..26ae15a
--- /dev/null
+++ b/src/proguard/shrink/InnerUsageMarker.java
@@ -0,0 +1,224 @@
+/* $Id: InnerUsageMarker.java,v 1.15 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.shrink;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This ClassFileVisitor recursively marks all inner classes
+ * that are being used in the classes it visits.
+ *
+ * @see UsageMarker
+ *
+ * @author Eric Lafortune
+ */
+public class InnerUsageMarker
+  implements ClassFileVisitor,
+             CpInfoVisitor,
+             AttrInfoVisitor,
+             InnerClassesInfoVisitor
+{
+    private boolean markingAttributes = true;
+    private boolean used;
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        boolean classUsed = UsageMarker.isUsed(programClassFile);
+
+        if (markingAttributes && classUsed)
+        {
+            markingAttributes = false;
+
+            // Check the inner class attribute.
+            programClassFile.attributesAccept(this);
+
+            markingAttributes = true;
+        }
+
+        // The return value.
+        used = classUsed;
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        // The return value.
+        used = true;
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+
+
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    {
+        boolean classUsed = UsageMarker.isUsed(classCpInfo);
+
+        if (!classUsed)
+        {
+            // The ClassCpInfo isn't marked as being used yet. But maybe it should
+            // be included as an interface, so check the actual class.
+            classCpInfo.referencedClassAccept(this);
+            classUsed = used;
+
+            if (classUsed)
+            {
+                // The class is being used. Mark the ClassCpInfo as being used
+                // as well.
+                UsageMarker.markAsUsed(classCpInfo);
+
+                markCpEntry(classFile, classCpInfo.u2nameIndex);
+            }
+        }
+
+        // The return value.
+        used = classUsed;
+    }
+
+
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo)
+    {
+        if (!UsageMarker.isUsed(utf8CpInfo))
+        {
+            UsageMarker.markAsUsed(utf8CpInfo);
+        }
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo) {}
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
+
+
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
+    {
+        boolean attributeUsed = false;
+
+        // Mark the interfaces that are being used.
+        for (int i = 0; i < innerClassesAttrInfo.u2numberOfClasses; i++)
+        {
+            // Check if the inner class entry is used.
+            visitInnerClassesInfo(classFile, innerClassesAttrInfo.classes[i]);
+            attributeUsed |= used;
+        }
+
+        if (attributeUsed)
+        {
+            // We got a positive used flag, so some inner class is being used.
+            // Mark this attribute as being used as well.
+            UsageMarker.markAsUsed(innerClassesAttrInfo);
+
+            markCpEntry(classFile, innerClassesAttrInfo.u2attrNameIndex);
+        }
+    }
+
+
+    // Implementations for InnerClassesInfoVisitor.
+
+    public void visitInnerClassesInfo(ClassFile classFile, InnerClassesInfo innerClassesInfo)
+    {
+        boolean innerClassesInfoUsed = UsageMarker.isUsed(innerClassesInfo);
+
+        if (!innerClassesInfoUsed)
+        {
+            int u2innerClassInfoIndex = innerClassesInfo.u2innerClassInfoIndex;
+            int u2outerClassInfoIndex = innerClassesInfo.u2outerClassInfoIndex;
+            int u2innerNameIndex      = innerClassesInfo.u2innerNameIndex;
+
+            innerClassesInfoUsed = true;
+
+            if (u2innerClassInfoIndex != 0)
+            {
+                // Check if the inner class is marked as being used.
+                markCpEntry(classFile, u2innerClassInfoIndex);
+                innerClassesInfoUsed &= used;
+            }
+
+            if (u2outerClassInfoIndex != 0)
+            {
+                // Check if the outer class is marked as being used.
+                markCpEntry(classFile, u2outerClassInfoIndex);
+                innerClassesInfoUsed &= used;
+            }
+
+            // If both the inner class and the outer class are marked as being
+            // used, then mark this InnerClassesInfo as well.
+            if (innerClassesInfoUsed)
+            {
+                UsageMarker.markAsUsed(innerClassesInfo);
+
+                if (u2innerNameIndex != 0)
+                {
+                    markCpEntry(classFile, u2innerNameIndex);
+                }
+            }
+        }
+
+        // The return value.
+        used = innerClassesInfoUsed;
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Marks the given constant pool entry of the given class. This includes
+     * visiting any other referenced constant pool entries.
+     */
+    private void markCpEntry(ClassFile classFile, int index)
+    {
+         classFile.constantPoolEntryAccept(index, this);
+    }
+}
diff --git a/src/proguard/shrink/InterfaceUsageMarker.java b/src/proguard/shrink/InterfaceUsageMarker.java
new file mode 100644
index 0000000..d8c84da
--- /dev/null
+++ b/src/proguard/shrink/InterfaceUsageMarker.java
@@ -0,0 +1,158 @@
+/* $Id: InterfaceUsageMarker.java,v 1.10 2004/12/11 16:35:23 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.shrink;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This ClassFileVisitor recursively marks all interface
+ * classes that are being used in the visited class.
+ *
+ * @see UsageMarker
+ *
+ * @author Eric Lafortune
+ */
+public class InterfaceUsageMarker
+  implements ClassFileVisitor,
+             CpInfoVisitor
+{
+    // A field acting as a return parameter for several methods.
+    private boolean used;
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        boolean classUsed         = UsageMarker.isUsed(programClassFile);
+        boolean classPossiblyUsed = UsageMarker.isPossiblyUsed(programClassFile);
+
+        if (classUsed || classPossiblyUsed)
+        {
+            // Mark the references to interfaces that are being used.
+            for (int i = 0; i < programClassFile.u2interfacesCount; i++)
+            {
+                // Check if the interface is used. Mark the constant pool entry
+                // if so.
+                markCpEntry(programClassFile, programClassFile.u2interfaces[i]);
+                classUsed |= used;
+            }
+
+            // Is this an interface with a preliminary mark?
+            if (classPossiblyUsed)
+            {
+                // Should it now be included?
+                if (classUsed)
+                {
+                    // At least one if this interface's interfaces is being used.
+                    // Mark this interface as well.
+                    UsageMarker.markAsUsed(programClassFile);
+
+                    // Mark this interface's name.
+                    markCpEntry(programClassFile, programClassFile.u2thisClass);
+
+                    // Mark the superclass (java/lang/Object).
+                    if (programClassFile.u2superClass != 0)
+                    {
+                        markCpEntry(programClassFile, programClassFile.u2superClass);
+                    }
+                }
+                else
+                {
+                    // Unmark this interface, so we don't bother looking at it again.
+                    UsageMarker.markAsUnused(programClassFile);
+                }
+            }
+        }
+
+        // The return value.
+        used = classUsed;
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        // The return value.
+        used = true;
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+
+
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    {
+        boolean classUsed = UsageMarker.isUsed(classCpInfo);
+
+        if (!classUsed)
+        {
+            // The ClassCpInfo isn't marked as being used yet. But maybe it should
+            // be included as an interface, so check the actual class.
+            classCpInfo.referencedClassAccept(this);
+            classUsed = used;
+
+            if (classUsed)
+            {
+                // The class is being used. Mark the ClassCpInfo as being used
+                // as well.
+                UsageMarker.markAsUsed(classCpInfo);
+
+                markCpEntry(classFile, classCpInfo.u2nameIndex);
+            }
+        }
+
+        // The return value.
+        used = classUsed;
+    }
+
+
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo)
+    {
+        if (!UsageMarker.isUsed(utf8CpInfo))
+        {
+            UsageMarker.markAsUsed(utf8CpInfo);
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Marks the given constant pool entry of the given class. This includes
+     * visiting any referenced objects.
+     */
+    private void markCpEntry(ClassFile classFile, int index)
+    {
+         classFile.constantPoolEntryAccept(index, this);
+    }
+}
diff --git a/src/proguard/shrink/UsageMarker.java b/src/proguard/shrink/UsageMarker.java
new file mode 100644
index 0000000..851f111
--- /dev/null
+++ b/src/proguard/shrink/UsageMarker.java
@@ -0,0 +1,824 @@
+/* $Id: UsageMarker.java,v 1.37 2004/12/19 21:03:54 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.shrink;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This ClassFileVisitor and MemberInfoVisitor recursively marks all
+ * classes and class elements that are being used.
+ *
+ * @see ClassFileShrinker
+ *
+ * @author Eric Lafortune
+ */
+public class UsageMarker
+  implements ClassFileVisitor,
+             MemberInfoVisitor,
+             CpInfoVisitor,
+             AttrInfoVisitor,
+             InnerClassesInfoVisitor,
+             ExceptionInfoVisitor,
+             LocalVariableInfoVisitor,
+             LocalVariableTypeInfoVisitor,
+             AnnotationVisitor,
+             ElementValueVisitor,
+             InstructionVisitor
+{
+    // A visitor info flag to indicate the ProgramMemberInfo object is being used,
+    // if its ClassFile can be determined as being used as well.
+    private static final Object POSSIBLY_USED = new Object();
+    // A visitor info flag to indicate the visitor accepter is being used.
+    private static final Object USED          = new Object();
+
+
+    private MyInterfaceUsageMarker interfaceUsageMarker = new MyInterfaceUsageMarker();
+//    private ClassFileVisitor       dynamicClassMarker   =
+//        new MultiClassFileVisitor(
+//        new ClassFileVisitor[]
+//        {
+//            this,
+//            new NamedMethodVisitor(ClassConstants.INTERNAL_METHOD_NAME_INIT,
+//                                   ClassConstants.INTERNAL_METHOD_TYPE_INIT,
+//                                   this)
+//        });
+
+    
+    // A field acting as a parameter to the visitMemberInfo method.
+    private boolean processing = false;
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        if (!isUsed(programClassFile))
+        {
+            // Mark this class.
+            markAsUsed(programClassFile);
+
+            // Mark this class's name.
+            markCpEntry(programClassFile, programClassFile.u2thisClass);
+
+            // Mark the superclass.
+            if (programClassFile.u2superClass != 0)
+            {
+                markCpEntry(programClassFile, programClassFile.u2superClass);
+            }
+
+            // Give the interfaces preliminary marks.
+            programClassFile.hierarchyAccept(false, false, true, false,
+                                             interfaceUsageMarker);
+
+            // Explicitly mark the <clinit> method.
+            programClassFile.methodAccept(ClassConstants.INTERNAL_METHOD_NAME_CLINIT,
+                                          ClassConstants.INTERNAL_METHOD_TYPE_CLINIT,
+                                          this);
+
+            // Explicitly mark the parameterless <init> method.
+            programClassFile.methodAccept(ClassConstants.INTERNAL_METHOD_NAME_INIT,
+                                          ClassConstants.INTERNAL_METHOD_TYPE_INIT,
+                                          this);
+
+            // Process all methods that have already been marked as possibly used.
+            processing = true;
+            programClassFile.methodsAccept(this);
+            processing = false;
+
+            // Mark the attributes.
+            programClassFile.attributesAccept(this);
+        }
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        if (!isUsed(libraryClassFile))
+        {
+            markAsUsed(libraryClassFile);
+
+            // We're not going to analyze all library code. We're assuming that
+            // if this class is being used, all of its methods will be used as
+            // well. We'll mark them as such (here and in all subclasses).
+
+            // Mark the superclass.
+            ClassFile superClass = libraryClassFile.superClass;
+            if (superClass != null)
+            {
+                superClass.accept(this);
+            }
+
+            // Mark the interfaces.
+            ClassFile[] interfaceClasses = libraryClassFile.interfaceClasses;
+            if (interfaceClasses != null)
+            {
+                for (int i = 0; i < interfaceClasses.length; i++)
+                {
+                    if (interfaceClasses[i] != null)
+                    {
+                        interfaceClasses[i].accept(this);
+                    }
+                }
+            }
+
+            // Mark all methods.
+            libraryClassFile.methodsAccept(this);
+        }
+    }
+
+
+    /**
+     * This ClassFileVisitor marks ProgramClassFile objects as possibly used,
+     * and it visits LibraryClassFile objects with its outer UsageMarker.
+     */
+    private class MyInterfaceUsageMarker implements ClassFileVisitor
+    {
+        public void visitProgramClassFile(ProgramClassFile programClassFile)
+        {
+            if (!isUsed(programClassFile))
+            {
+                // We can't process the interface yet, because it might not
+                // be required. Give it a preliminary mark.
+                markAsPossiblyUsed(programClassFile);
+            }
+        }
+
+        public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+        {
+            // Make sure all library interface methods are marked.
+            UsageMarker.this.visitLibraryClassFile(libraryClassFile);
+        }
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        if (!isUsed(programFieldInfo))
+        {
+            markAsUsed(programFieldInfo);
+
+            // Mark the name and descriptor.
+            markCpEntry(programClassFile, programFieldInfo.u2nameIndex);
+            markCpEntry(programClassFile, programFieldInfo.u2descriptorIndex);
+
+            // Mark the attributes.
+            programFieldInfo.attributesAccept(programClassFile, this);
+
+            // Mark the classes referenced in the descriptor string.
+            programFieldInfo.referencedClassesAccept(this);
+        }
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        if (!isUsed(programMethodInfo))
+        {
+            // Are the method and its class used?
+            if (processing ? isPossiblyUsed(programMethodInfo) :
+                             isUsed(programClassFile))
+            {
+                markAsUsed(programMethodInfo);
+
+                // Remember the processing flag.
+                boolean oldProcessing = processing;
+                processing = false;
+
+                // Mark the name and descriptor.
+                markCpEntry(programClassFile, programMethodInfo.u2nameIndex);
+                markCpEntry(programClassFile, programMethodInfo.u2descriptorIndex);
+
+                // Mark the attributes.
+                programMethodInfo.attributesAccept(programClassFile, this);
+
+                // Mark the classes referenced in the descriptor string.
+                programMethodInfo.referencedClassesAccept(this);
+
+                // Restore the processing flag.
+                processing = oldProcessing;
+
+                // If the method is being called, mark its hierarchy.
+                if (!processing)
+                {
+                    markMethodHierarchy(programClassFile, programMethodInfo);
+                }
+            }
+            else if (!processing && !isPossiblyUsed(programMethodInfo))
+            {
+                // We can't process the class member yet, because the class
+                // file isn't marked as being used (yet). Give it a
+                // preliminary mark.
+                markAsPossiblyUsed(programMethodInfo);
+
+                // The method is being called. Mark its hierarchy.
+                markMethodHierarchy(programClassFile, programMethodInfo);
+            }
+        }
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    {
+        if (!isUsed(libraryMethodInfo))
+        {
+            markAsUsed(libraryMethodInfo);
+            
+            markMethodHierarchy(libraryClassFile, libraryMethodInfo);
+        }
+    }
+
+
+    private void markMethodHierarchy(ClassFile classFile, MethodInfo methodInfo)
+    {
+        if ((methodInfo.getAccessFlags() &
+             (ClassConstants.INTERNAL_ACC_PRIVATE |
+              ClassConstants.INTERNAL_ACC_FINAL)) == 0)
+        {
+            String name = methodInfo.getName(classFile);
+            String type = methodInfo.getDescriptor(classFile);
+
+            if (!name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) &&
+                !name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT))
+            {
+                // Mark all implementations of the method.
+                //
+                // For an abstract method:
+                //   First go to all concrete implementations or extensions of
+                //   the interface or abstract class.
+                //   From there, travel up and down the class hierarchy to mark
+                //   the method.
+                //
+                //   This way, we're also catching retro-fitted interfaces,
+                //   where a class's implementation of an interface method is
+                //   hiding higher up its class hierarchy.
+                //
+                // For a concrete method:
+                //   Simply mark all overriding implementations down the
+                //   class hierarchy.
+                classFile.accept(
+                    (classFile.getAccessFlags()  & ClassConstants.INTERNAL_ACC_INTERFACE) != 0 ||
+                    (methodInfo.getAccessFlags() & ClassConstants.INTERNAL_ACC_ABSTRACT)  != 0 ?
+    
+                        (ClassFileVisitor)
+                        new ConcreteClassFileDownTraveler(
+                        new ClassFileHierarchyTraveler(true, true, false, true,
+                        new NamedMethodVisitor(name, type,
+                        new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, this)))) :
+    
+                        (ClassFileVisitor)
+                        new ClassFileHierarchyTraveler(false, false, false, true,
+                        new NamedMethodVisitor(name, type,
+                        new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, this))));
+            }
+        }
+    }
+
+
+    // Implementations for CpInfoVisitor.
+
+    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo)
+    {
+        if (!isUsed(integerCpInfo))
+        {
+            markAsUsed(integerCpInfo);
+        }
+    }
+
+
+    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo)
+    {
+        if (!isUsed(longCpInfo))
+        {
+            markAsUsed(longCpInfo);
+        }
+    }
+
+
+    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo)
+    {
+        if (!isUsed(floatCpInfo))
+        {
+            markAsUsed(floatCpInfo);
+        }
+    }
+
+
+    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo)
+    {
+        if (!isUsed(doubleCpInfo))
+        {
+            markAsUsed(doubleCpInfo);
+        }
+    }
+
+
+    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
+    {
+        if (!isUsed(stringCpInfo))
+        {
+            markAsUsed(stringCpInfo);
+
+            markCpEntry(classFile, stringCpInfo.u2stringIndex);
+
+            // Mark the referenced class and its parameterless constructor,
+            // if the string is being used in a Class.forName construct.
+            //stringCpInfo.referencedClassAccept(dynamicClassMarker);
+
+            // Mark the referenced class.
+            stringCpInfo.referencedClassAccept(this);
+        }
+    }
+
+
+    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo)
+    {
+        if (!isUsed(utf8CpInfo))
+        {
+            markAsUsed(utf8CpInfo);
+        }
+    }
+
+
+    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    {
+        visitRefCpInfo(classFile, fieldrefCpInfo);
+    }
+
+
+    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    {
+        visitRefCpInfo(classFile, interfaceMethodrefCpInfo);
+    }
+
+
+    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    {
+        visitRefCpInfo(classFile, methodrefCpInfo);
+    }
+
+
+    private void visitRefCpInfo(ClassFile classFile, RefCpInfo methodrefCpInfo)
+    {
+        if (!isUsed(methodrefCpInfo))
+        {
+            markAsUsed(methodrefCpInfo);
+
+            markCpEntry(classFile, methodrefCpInfo.u2classIndex);
+            markCpEntry(classFile, methodrefCpInfo.u2nameAndTypeIndex);
+
+            // When compiled with "-target 1.2" or higher, the class or
+            // interface actually containing the referenced method may be
+            // higher up the hierarchy. Make sure it's marked, in case it
+            // isn't used elsewhere.
+            methodrefCpInfo.referencedClassAccept(this);
+
+            // Mark the referenced method itself.
+            methodrefCpInfo.referencedMemberInfoAccept(this);
+        }
+    }
+
+
+    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    {
+        if (!isUsed(classCpInfo))
+        {
+            markAsUsed(classCpInfo);
+
+            markCpEntry(classFile, classCpInfo.u2nameIndex);
+
+            // Mark the referenced class itself.
+            classCpInfo.referencedClassAccept(this);
+        }
+    }
+
+
+    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo)
+    {
+        if (!isUsed(nameAndTypeCpInfo))
+        {
+            markAsUsed(nameAndTypeCpInfo);
+
+            markCpEntry(classFile, nameAndTypeCpInfo.u2nameIndex);
+            markCpEntry(classFile, nameAndTypeCpInfo.u2descriptorIndex);
+
+            // Mark the classes referenced in the descriptor string.
+            nameAndTypeCpInfo.referencedClassesAccept(this);
+        }
+    }
+
+
+    // Implementations for AttrInfoVisitor.
+    // Note that attributes are typically only referenced once, so we don't
+    // test if they have been marked already.
+
+    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo)
+    {
+        // This is the best we can do for unknown attributes.
+        markAsUsed(unknownAttrInfo);
+
+        markCpEntry(classFile, unknownAttrInfo.u2attrNameIndex);
+    }
+
+
+    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
+    {
+        // Don't mark the attribute and its name yet. We may mark it later, in
+        // InnerUsageMarker.
+        //markAsUsed(innerClassesAttrInfo);
+
+        //markCpEntry(classFile, innerClassesAttrInfo.u2attrNameIndex);
+        innerClassesAttrInfo.innerClassEntriesAccept(classFile, this);
+    }
+
+
+    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
+    {
+        markAsUsed(enclosingMethodAttrInfo);
+
+        markCpEntry(classFile, enclosingMethodAttrInfo.u2attrNameIndex);
+        markCpEntry(classFile, enclosingMethodAttrInfo.u2classIndex);
+
+        if (enclosingMethodAttrInfo.u2nameAndTypeIndex != 0)
+        {
+            markCpEntry(classFile, enclosingMethodAttrInfo.u2nameAndTypeIndex);
+        }
+    }
+
+
+    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo)
+    {
+        markAsUsed(constantValueAttrInfo);
+
+        markCpEntry(classFile, constantValueAttrInfo.u2attrNameIndex);
+        markCpEntry(classFile, constantValueAttrInfo.u2constantValueIndex);
+    }
+
+
+    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo)
+    {
+        markAsUsed(exceptionsAttrInfo);
+
+        markCpEntry(classFile, exceptionsAttrInfo.u2attrNameIndex);
+
+        // Mark the constant pool entries referenced by the exceptions.
+        exceptionsAttrInfo.exceptionEntriesAccept((ProgramClassFile)classFile, this);
+    }
+
+
+    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    {
+        markAsUsed(codeAttrInfo);
+
+        markCpEntry(classFile, codeAttrInfo.u2attrNameIndex);
+
+        // Mark the constant pool entries referenced by the instructions,
+        // and the exceptions and attributes.
+        codeAttrInfo.instructionsAccept(classFile, methodInfo, this);
+        codeAttrInfo.exceptionsAccept(classFile, methodInfo, this);
+        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
+    }
+
+
+    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo)
+    {
+        markAsUsed(lineNumberTableAttrInfo);
+
+        markCpEntry(classFile, lineNumberTableAttrInfo.u2attrNameIndex);
+    }
+
+
+    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
+    {
+        markAsUsed(localVariableTableAttrInfo);
+
+        markCpEntry(classFile, localVariableTableAttrInfo.u2attrNameIndex);
+
+        // Mark the constant pool entries referenced by the local variables.
+        localVariableTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+    }
+
+
+    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
+    {
+        markAsUsed(localVariableTypeTableAttrInfo);
+
+        markCpEntry(classFile, localVariableTypeTableAttrInfo.u2attrNameIndex);
+
+        // Mark the constant pool entries referenced by the local variable types.
+        localVariableTypeTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+    }
+
+
+    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo)
+    {
+        markAsUsed(sourceFileAttrInfo);
+
+        markCpEntry(classFile, sourceFileAttrInfo.u2attrNameIndex);
+        markCpEntry(classFile, sourceFileAttrInfo.u2sourceFileIndex);
+    }
+
+
+    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo)
+    {
+        markAsUsed(sourceDirAttrInfo);
+
+        markCpEntry(classFile, sourceDirAttrInfo.u2attrNameIndex);
+        markCpEntry(classFile, sourceDirAttrInfo.u2sourceDirIndex);
+    }
+
+
+    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo)
+    {
+        markAsUsed(deprecatedAttrInfo);
+
+        markCpEntry(classFile, deprecatedAttrInfo.u2attrNameIndex);
+    }
+
+
+    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo)
+    {
+        markAsUsed(syntheticAttrInfo);
+
+        markCpEntry(classFile, syntheticAttrInfo.u2attrNameIndex);
+    }
+
+
+    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
+    {
+        markAsUsed(signatureAttrInfo);
+
+        markCpEntry(classFile, signatureAttrInfo.u2attrNameIndex);
+        markCpEntry(classFile, signatureAttrInfo.u2signatureIndex);
+    }
+
+
+    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
+    {
+        markAsUsed(runtimeVisibleAnnotationsAttrInfo);
+
+        markCpEntry(classFile, runtimeVisibleAnnotationsAttrInfo.u2attrNameIndex);
+
+        // Mark the constant pool entries referenced by the annotations.
+        runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
+    {
+        markAsUsed(runtimeInvisibleAnnotationsAttrInfo);
+
+        markCpEntry(classFile, runtimeInvisibleAnnotationsAttrInfo.u2attrNameIndex);
+
+        // Mark the constant pool entries referenced by the annotations.
+        runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
+    {
+        markAsUsed(runtimeVisibleParameterAnnotationsAttrInfo);
+
+        markCpEntry(classFile, runtimeVisibleParameterAnnotationsAttrInfo.u2attrNameIndex);
+
+        // Mark the constant pool entries referenced by the annotations.
+        runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
+    {
+        markAsUsed(runtimeInvisibleParameterAnnotationsAttrInfo);
+
+        markCpEntry(classFile, runtimeInvisibleParameterAnnotationsAttrInfo.u2attrNameIndex);
+
+        // Mark the constant pool entries referenced by the annotations.
+        runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+    }
+
+
+    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
+    {
+        markAsUsed(annotationDefaultAttrInfo);
+
+        markCpEntry(classFile, annotationDefaultAttrInfo.u2attrNameIndex);
+
+        // Mark the constant pool entries referenced by the element value.
+        annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo)
+    {
+        markAsUsed(exceptionInfo);
+
+        if (exceptionInfo.u2catchType != 0)
+        {
+            markCpEntry(classFile, exceptionInfo.u2catchType);
+        }
+    }
+
+
+    // Implementations for InnerClassesInfoVisitor.
+
+    public void visitInnerClassesInfo(ClassFile classFile, InnerClassesInfo innerClassesInfo)
+    {
+        // At this point, we only mark outer classes of this class.
+        // Inner class can be marked later, by InnerUsageMarker.
+        if (innerClassesInfo.u2innerClassInfoIndex == 0 &&
+            classFile.getName().equals(classFile.getCpClassNameString(innerClassesInfo.u2innerClassInfoIndex)))
+        {
+            markAsUsed(innerClassesInfo);
+
+            if (innerClassesInfo.u2innerClassInfoIndex != 0)
+            {
+                markCpEntry(classFile, innerClassesInfo.u2innerClassInfoIndex);
+            }
+
+            if (innerClassesInfo.u2outerClassInfoIndex != 0)
+            {
+                markCpEntry(classFile, innerClassesInfo.u2outerClassInfoIndex);
+            }
+
+            if (innerClassesInfo.u2innerNameIndex != 0)
+            {
+                markCpEntry(classFile, innerClassesInfo.u2innerNameIndex);
+            }
+        }
+    }
+
+
+    // Implementations for LocalVariableInfoVisitor.
+
+    public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo)
+    {
+        markCpEntry(classFile, localVariableInfo.u2nameIndex);
+        markCpEntry(classFile, localVariableInfo.u2descriptorIndex);
+    }
+
+
+    // Implementations for LocalVariableTypeInfoVisitor.
+
+    public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo)
+    {
+        markCpEntry(classFile, localVariableTypeInfo.u2nameIndex);
+        markCpEntry(classFile, localVariableTypeInfo.u2signatureIndex);
+    }
+
+
+    // Implementations for AnnotationVisitor.
+
+    public void visitAnnotation(ClassFile classFile, Annotation annotation)
+    {
+        markCpEntry(classFile, annotation.u2typeIndex);
+
+        // Mark the constant pool entries referenced by the element values.
+        annotation.elementValuesAccept(classFile, this);
+    }
+
+
+    // Implementations for ElementValueVisitor.
+
+    public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue)
+    {
+        if (constantElementValue.u2elementName != 0)
+        {
+            markCpEntry(classFile, constantElementValue.u2elementName);
+        }
+
+        markCpEntry(classFile, constantElementValue.u2constantValueIndex);
+    }
+
+
+    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+    {
+        if (enumConstantElementValue.u2elementName != 0)
+        {
+            markCpEntry(classFile, enumConstantElementValue.u2elementName);
+        }
+
+        markCpEntry(classFile, enumConstantElementValue.u2typeNameIndex);
+        markCpEntry(classFile, enumConstantElementValue.u2constantNameIndex);
+    }
+
+
+    public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
+    {
+        if (classElementValue.u2elementName != 0)
+        {
+            markCpEntry(classFile, classElementValue.u2elementName);
+        }
+
+        // Mark the referenced class constant pool entry.
+        markCpEntry(classFile, classElementValue.u2classInfoIndex);
+    }
+
+
+    public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue)
+    {
+        if (annotationElementValue.u2elementName != 0)
+        {
+            markCpEntry(classFile, annotationElementValue.u2elementName);
+        }
+
+        // Mark the constant pool entries referenced by the annotation.
+        annotationElementValue.annotationAccept(classFile, this);
+    }
+
+
+    public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
+    {
+        if (arrayElementValue.u2elementName != 0)
+        {
+            markCpEntry(classFile, arrayElementValue.u2elementName);
+        }
+
+        // Mark the constant pool entries referenced by the element values.
+        arrayElementValue.elementValuesAccept(classFile, annotation, this);
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
+    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
+    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
+    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
+    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+
+
+    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    {
+        markCpEntry(classFile, cpInstruction.cpIndex);
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Marks the given constant pool entry of the given class. This includes
+     * visiting any referenced objects.
+     */
+    private void markCpEntry(ClassFile classFile, int index)
+    {
+         classFile.constantPoolEntryAccept(index, this);
+    }
+
+
+    static void markAsUnused(VisitorAccepter visitorAccepter)
+    {
+        visitorAccepter.setVisitorInfo(null);
+    }
+
+
+    static void markAsPossiblyUsed(VisitorAccepter visitorAccepter)
+    {
+        visitorAccepter.setVisitorInfo(POSSIBLY_USED);
+    }
+
+
+    static boolean isPossiblyUsed(VisitorAccepter visitorAccepter)
+    {
+        return visitorAccepter.getVisitorInfo() == POSSIBLY_USED;
+    }
+
+
+    static void markAsUsed(VisitorAccepter visitorAccepter)
+    {
+        visitorAccepter.setVisitorInfo(USED);
+    }
+
+
+    static boolean isUsed(VisitorAccepter visitorAccepter)
+    {
+        return visitorAccepter.getVisitorInfo() == USED;
+    }
+}
diff --git a/src/proguard/shrink/UsagePrinter.java b/src/proguard/shrink/UsagePrinter.java
new file mode 100644
index 0000000..18abcaa
--- /dev/null
+++ b/src/proguard/shrink/UsagePrinter.java
@@ -0,0 +1,174 @@
+/* $Id: UsagePrinter.java,v 1.16 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.shrink;
+
+import proguard.classfile.*;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+
+
+/**
+ * This ClassFileVisitor prints out the class files and class members that have been
+ * marked as being used (or not used).
+ *
+ * @see UsageMarker
+ *
+ * @author Eric Lafortune
+ */
+public class UsagePrinter
+  implements ClassFileVisitor,
+             MemberInfoVisitor
+{
+    private boolean     printUnusedItems;
+    private PrintStream ps;
+
+    // A field to remember the class name, if a header is needed for class members.
+    private String      className;
+
+
+    /**
+     * Creates a new UsagePrinter that prints to <code>System.out</code>.
+     * @param printUsedItems a flag that indicates whether only unused items
+     * should be printed, or alternatively, only used items.
+     */
+    public UsagePrinter(boolean printUnusedItems)
+    {
+        this(printUnusedItems, System.out);
+    }
+
+
+    /**
+     * Creates a new UsagePrinter that prints to the given stream.
+     * @param printUsedItems a flag that indicates whether only unused items
+     * should be printed, or alternatively, only used items.
+     * @param printStream the stream to which to print
+     */
+    public UsagePrinter(boolean printUnusedItems, PrintStream printStream)
+    {
+        this.printUnusedItems = printUnusedItems;
+        this.ps               = printStream;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        if (UsageMarker.isUsed(programClassFile))
+        {
+            if (printUnusedItems)
+            {
+                className = programClassFile.getName();
+
+                programClassFile.fieldsAccept(this);
+                programClassFile.methodsAccept(this);
+
+                className = null;
+            }
+            else
+            {
+                ps.println(ClassUtil.externalClassName(programClassFile.getName()));
+            }
+        }
+        else
+        {
+            if (printUnusedItems)
+            {
+                ps.println(ClassUtil.externalClassName(programClassFile.getName()));
+            }
+        }
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+    }
+
+
+    // Implementations for MemberInfoVisitor.
+
+    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    {
+        if (UsageMarker.isUsed(programFieldInfo) ^ printUnusedItems)
+        {
+            printClassNameHeader();
+
+            ps.println("    " +
+                       lineNumberRange(programClassFile, programFieldInfo) +
+                       ClassUtil.externalFullFieldDescription(
+                           programFieldInfo.getAccessFlags(),
+                           programFieldInfo.getName(programClassFile),
+                           programFieldInfo.getDescriptor(programClassFile)));
+        }
+    }
+
+
+    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    {
+        if (UsageMarker.isUsed(programMethodInfo) ^ printUnusedItems)
+        {
+            printClassNameHeader();
+
+            ps.println("    " +
+                       lineNumberRange(programClassFile, programMethodInfo) +
+                       ClassUtil.externalFullMethodDescription(
+                           programClassFile.getName(),
+                           programMethodInfo.getAccessFlags(),
+                           programMethodInfo.getName(programClassFile),
+                           programMethodInfo.getDescriptor(programClassFile)));
+        }
+    }
+
+
+    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+
+
+    // Small utility methods.
+
+    /**
+     * Prints the class name field. The field is then cleared, so it is not
+     * printed again.
+     */
+    private void printClassNameHeader()
+    {
+        if (className != null)
+        {
+            ps.println(ClassUtil.externalClassName(className) + ":");
+            className = null;
+        }
+    }
+
+
+    /**
+     * Returns the line number range of the given class member, followed by a
+     * colon, or just an empty String if no range is available.
+     */
+    private static String lineNumberRange(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    {
+        String range = programMemberInfo.getLineNumberRange(programClassFile);
+        return range != null ?
+            (range + ":") :
+            "";
+    }
+}
diff --git a/src/proguard/shrink/UsedClassFileFilter.java b/src/proguard/shrink/UsedClassFileFilter.java
new file mode 100644
index 0000000..6b33c62
--- /dev/null
+++ b/src/proguard/shrink/UsedClassFileFilter.java
@@ -0,0 +1,65 @@
+/* $Id: UsedClassFileFilter.java,v 1.9 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.shrink;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This ClassFileVisitor delegates all its method calls to another
+ * ClassFileVisitor, but only for ClassFile objects that are marked as used.
+ *
+ * @see UsageMarker
+ *
+ * @author Eric Lafortune
+ */
+public class UsedClassFileFilter
+  implements ClassFileVisitor
+{
+    ClassFileVisitor classFileVisitor;
+
+
+    public UsedClassFileFilter(ClassFileVisitor classFileVisitor)
+    {
+        this.classFileVisitor = classFileVisitor;
+    }
+
+
+    // Implementations for ClassFileVisitor.
+
+    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    {
+        if (UsageMarker.isUsed(programClassFile))
+        {
+            classFileVisitor.visitProgramClassFile(programClassFile);
+        }
+    }
+
+
+    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    {
+        if (UsageMarker.isUsed(libraryClassFile))
+        {
+            classFileVisitor.visitLibraryClassFile(libraryClassFile);
+        }
+    }
+}
diff --git a/src/proguard/shrink/package.html b/src/proguard/shrink/package.html
new file mode 100644
index 0000000..8973198
--- /dev/null
+++ b/src/proguard/shrink/package.html
@@ -0,0 +1,3 @@
+<body>
+This package contains classes to perform shrinking of class files.
+</body>
diff --git a/src/proguard/util/BasicListMatcher.java b/src/proguard/util/BasicListMatcher.java
new file mode 100644
index 0000000..3a3c44d
--- /dev/null
+++ b/src/proguard/util/BasicListMatcher.java
@@ -0,0 +1,148 @@
+/* $Id: BasicListMatcher.java,v 1.7 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.util;
+
+import java.util.*;
+
+
+/**
+ * This StringMatcher tests whether strings match an entry in a given list of
+ * regular expressions. The list is given as a comma-separated string or as a
+ * List of strings. An exclamation mark preceding a list entry acts as a
+ * negator: if the expression matches, a negative match is returned, without
+ * considering any subsequent entries. If none of the entries match, a positive
+ * match is returned depending on whether the last regular expression had a
+ * negator or not.
+ * <p>
+ * The individual regular expression matching is delegated to a StringMatcher
+ * that is created by the {@link #createBasicMatcher(String}} method. If it is
+ * not overridden, this method returns a BasicMatcher.
+ *
+ * @see BasicMatcher
+ * @author Eric Lafortune
+ */
+public class BasicListMatcher implements StringMatcher
+{
+    private static final char REGULAR_EXPRESSION_SEPARATOR = ',';
+    private static final char REGULAR_EXPRESSION_NEGATOR   = '!';
+
+    private StringMatcher[] regularExpressionMatchers;
+    private boolean[]       negatedRegularExpressions;
+
+
+    /**
+     * Creates a new BasicListMatcher.
+     * @param regularExpression the comma-separated list of regular expressions
+     *                          against which strings will be matched.
+     */
+    public BasicListMatcher(String regularExpression)
+    {
+        this(ListUtil.commaSeparatedList(regularExpression));
+    }
+
+
+    /**
+     * Creates a new BasicListMatcher.
+     * @param regularExpressionList the list of regular expressions against which
+     *                              strings will be matched.
+     */
+    public BasicListMatcher(List regularExpressionList)
+    {
+        // Collect the regular expressions in arrays.
+        int regularExpressionCount = regularExpressionList.size();
+
+        regularExpressionMatchers = new StringMatcher[regularExpressionCount];
+        negatedRegularExpressions = new boolean[regularExpressionCount];
+
+        for (int index = 0; index < regularExpressionCount; index++)
+        {
+            String regularExpression = (String)regularExpressionList.get(index);
+
+            // Does the regular expression start with an exclamation mark?
+            if (regularExpression.length() > 0 &&
+                regularExpression.charAt(0) == REGULAR_EXPRESSION_NEGATOR)
+            {
+                // Trim the regular expression.
+                regularExpression = regularExpression.substring(1);
+
+                // Remember the negator.
+                negatedRegularExpressions[index] = true;
+            }
+
+            regularExpressionMatchers[index] =
+                createBasicMatcher(regularExpression);
+        }
+    }
+
+
+    /**
+     * Creates a new StringMatcher for the given regular expression.
+     */
+    protected StringMatcher createBasicMatcher(String regularExpression)
+    {
+        return new BasicMatcher(regularExpression);
+    }
+
+
+    // Implementations for StringMatcher.
+
+    public boolean matches(String string)
+    {
+        boolean result = true;
+
+        for (int index = 0; index < regularExpressionMatchers.length; index++)
+        {
+            result = negatedRegularExpressions[index];
+
+            if (regularExpressionMatchers[index].matches(string))
+            {
+                return !result;
+            }
+        }
+
+        return result;
+    }
+
+
+    /**
+     * A main method for testing string matching.
+     */
+    public static void main(String[] args)
+    {
+        try
+        {
+            System.out.println("Regular expression ["+args[0]+"]");
+            BasicListMatcher matcher =
+                new BasicListMatcher(args[0]);
+
+            for (int index = 1; index < args.length; index++)
+            {
+                String string = args[index];
+                System.out.print("String             ["+string+"]");
+                System.out.println(" -> match = "+matcher.matches(args[index]));
+            }
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/src/proguard/util/BasicMatcher.java b/src/proguard/util/BasicMatcher.java
new file mode 100644
index 0000000..ee173eb
--- /dev/null
+++ b/src/proguard/util/BasicMatcher.java
@@ -0,0 +1,360 @@
+/* $Id: BasicMatcher.java,v 1.6 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.util;
+
+import java.util.*;
+
+
+/**
+ * This StringMatcher tests whether strings match a given regular
+ * expression. Supported wildcards are
+ * <ul>
+ * <li>'?'  for a single Java identifier character or other wildcard
+ *          matching character,
+ * <li>'*'  for any number of Java identifier characters or other wildcard
+ *          matching characters, and
+ * <li>'**' for any number of Java identifier characters or extended wildcard
+ *          matching characters,
+ * <li>'%'  for a single special wildcard matching character.
+ * </ul>
+ * The sets of wildcard characters, extended wildcard characters, and special
+ * wildcard characters can be defined by the user.
+ *
+ * @author Eric Lafortune
+ */
+public class BasicMatcher implements StringMatcher
+{
+    private static final String SINGLE_CHARACTER_WILDCARD     = "?";
+    private static final String MULTIPLE_CHARACTERS_WILDCARD1 = "*";
+    private static final String MULTIPLE_CHARACTERS_WILDCARD2 = "**";
+    private static final String SPECIAL_CHARACTER_WILDCARD    = "%";
+
+    private String[] expressionParts;
+    private char[]   wildcardCharacters;
+    private char[]   extendedWildcardCharacters;
+    private char[]   specialWildcardCharacters;
+
+
+    /**
+     * Creates a new BasicMatcher without extra wildcard matching
+     * characters.
+     * @param regularExpression the regular expression against which strings
+     *                          will be matched.
+     */
+    public BasicMatcher(String regularExpression)
+    {
+        this(regularExpression, null, null, null);
+    }
+
+
+    /**
+     * Creates a new BasicMatcher.
+     * @param regularExpression the regular expression against which strings
+     *                          will be matched.
+     * @param wildcardCharacters an optional extra list of wildcard matching
+     *                           characters.
+     * @param wildcardCharacters an optional extra list of extended wildcard
+     *                           matching characters.
+     */
+    public BasicMatcher(String regularExpression,
+                        char[] wildcardCharacters,
+                        char[] extendedWildcardCharacters,
+                        char[] specialWildcardCharacters)
+    {
+        this.wildcardCharacters         = wildcardCharacters;
+        this.extendedWildcardCharacters = extendedWildcardCharacters;
+        this.specialWildcardCharacters  = specialWildcardCharacters;
+
+        // Split the given regular expression into an array of parts: "?",
+        // "*", "**", "%", and simple text strings.
+
+        // A List to collect the subsequent regular expression parts.
+        List expressionPartsList = new ArrayList();
+
+        String wildcard      = null;
+        int    previousIndex = 0;
+        int    index         = 0;
+        int    regularExpressionLength = regularExpression.length();
+        while (index < regularExpressionLength)
+        {
+            wildcard =
+                regularExpression.regionMatches(index, MULTIPLE_CHARACTERS_WILDCARD2, 0, MULTIPLE_CHARACTERS_WILDCARD2.length()) ? MULTIPLE_CHARACTERS_WILDCARD2 :
+                regularExpression.regionMatches(index, MULTIPLE_CHARACTERS_WILDCARD1, 0, MULTIPLE_CHARACTERS_WILDCARD1.length()) ? MULTIPLE_CHARACTERS_WILDCARD1 :
+                regularExpression.regionMatches(index, SINGLE_CHARACTER_WILDCARD,     0, SINGLE_CHARACTER_WILDCARD.length()) ?     SINGLE_CHARACTER_WILDCARD     :
+                regularExpression.regionMatches(index, SPECIAL_CHARACTER_WILDCARD,    0, SINGLE_CHARACTER_WILDCARD.length()) ?     SPECIAL_CHARACTER_WILDCARD    :
+                                                                                                                                   null;
+            if (wildcard != null)
+            {
+                // Add the simple text string that we've skipped.
+                if (previousIndex < index)
+                {
+                    expressionPartsList.add(regularExpression.substring(previousIndex, index));
+                }
+
+                // Add the wildcard that we've found.
+                expressionPartsList.add(wildcard);
+
+                // We'll continue parsing after this wildcard.
+                index += wildcard.length();
+                previousIndex = index;
+            }
+            else
+            {
+                // We'll continue parsing at the next character.
+                index++;
+            }
+        }
+
+        // Add the final simple text string that we've skipped, if any.
+        if (wildcard == null)
+        {
+            expressionPartsList.add(regularExpression.substring(previousIndex));
+        }
+
+        // Copy the List into the array.
+        expressionParts = new String[expressionPartsList.size()];
+        expressionPartsList.toArray(expressionParts);
+    }
+
+
+    // Implementations for StringMatcher.
+
+    public boolean matches(String string)
+    {
+        return matches(string, 0, 0);
+    }
+
+
+    /**
+     * Tries to match the given string, starting at the given index, with the
+     * regular expression parts starting at the given index.
+     */
+    private boolean matches(String string,
+                            int    stringStartIndex,
+                            int    expressionIndex)
+    {
+        // Are we out of expression parts?
+        if (expressionIndex == expressionParts.length)
+        {
+            // There's a match, at least if we're at the end of the string as well.
+            return stringStartIndex == string.length();
+        }
+
+        String expressionPart = expressionParts[expressionIndex];
+
+        // Did we get a wildcard of some sort?
+        if (expressionPart.equals(SINGLE_CHARACTER_WILDCARD))
+        {
+            // Do we have any characters left to match?
+            if (stringStartIndex == string.length())
+            {
+                // We've run out of characters.
+                return false;
+            }
+
+            // Make sure we're matching an allowed character and then check if
+            // the rest of the expression parts match.
+            return
+                matchesWildcard(string.charAt(stringStartIndex)) &&
+                matches(string, stringStartIndex + 1, expressionIndex + 1);
+        }
+        else if (expressionPart.equals(MULTIPLE_CHARACTERS_WILDCARD1))
+        {
+            // Try out all possible matches for '*', not matching the package
+            // separator.
+            for (int stringEndIndex = stringStartIndex;
+                 stringEndIndex <= string.length();
+                 stringEndIndex++)
+            {
+                // Are we matching some characters already?
+                if (stringEndIndex > stringStartIndex)
+                {
+                    // Make sure we don't start matching the wrong characters.
+                    if (!matchesWildcard(string.charAt(stringEndIndex-1)))
+                    {
+                        // We can never get a match.
+                        return false;
+                    }
+                }
+
+                // Continue looking for a match of the next expression part,
+                // starting from the end index.
+                if (matches(string, stringEndIndex, expressionIndex + 1))
+                {
+                    return true;
+                }
+            }
+
+            // We could get a match for '*', but not for the rest of the
+            // expression parts.
+            return false;
+        }
+        else if (expressionPart.equals(MULTIPLE_CHARACTERS_WILDCARD2))
+        {
+            // Try out all possible matches for '**'.
+            for (int stringEndIndex = stringStartIndex;
+                 stringEndIndex <= string.length();
+                 stringEndIndex++)
+            {
+                // Are we matching some characters already?
+                if (stringEndIndex > stringStartIndex)
+                {
+                    // Make sure we don't start matching the wrong characters.
+                    if (!matchesExtendedWildcard(string.charAt(stringEndIndex-1)))
+                    {
+                        // We can never get a match.
+                        return false;
+                    }
+                }
+
+                // Continue looking for a match of the next expression part,
+                // starting from this index.
+                if (matches(string, stringEndIndex, expressionIndex + 1))
+                {
+                    return true;
+                }
+            }
+
+            // We could get a match for '**', but not for the rest of the
+            // expression parts.
+            return stringStartIndex == string.length();
+        }
+        else if (expressionPart.equals(SPECIAL_CHARACTER_WILDCARD))
+        {
+            // Do we have any characters left to match?
+            if (stringStartIndex == string.length())
+            {
+                // We've run out of characters.
+                return false;
+            }
+
+            // Make sure we're matching an allowed character and then check if
+            // the rest of the expression parts match.
+            return
+                matchesSpecialWildcard(string.charAt(stringStartIndex)) &&
+                matches(string, stringStartIndex + 1, expressionIndex + 1);
+        }
+        else
+        {
+            // The expression part is a simple text string. Check if it matches,
+            // and if the rest of the expression parts match.
+            int expressionPartLength = expressionPart.length();
+            return
+                string.regionMatches(stringStartIndex, expressionPart, 0, expressionPartLength) &&
+                matches(string, stringStartIndex + expressionPartLength, expressionIndex + 1);
+        }
+    }
+
+
+    /**
+     * Returns whether the given character matches a simple '?' or '*' wildcard.
+     */
+    private boolean matchesWildcard(char character)
+    {
+        if (Character.isJavaIdentifierPart(character))
+        {
+            return true;
+        }
+
+        if (wildcardCharacters != null)
+        {
+            for (int index = 0; index < wildcardCharacters.length; index++)
+            {
+                if (character == wildcardCharacters[index])
+                {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Returns whether the given character matches an extended '**' wildcard.
+     */
+    private boolean matchesExtendedWildcard(char character)
+    {
+        if (matchesWildcard(character))
+        {
+            return true;
+        }
+
+        if (extendedWildcardCharacters != null)
+        {
+            for (int index = 0; index < extendedWildcardCharacters.length; index++)
+            {
+                if (character == extendedWildcardCharacters[index])
+                {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Returns whether the given character matches a special '%' wildcard.
+     */
+    private boolean matchesSpecialWildcard(char character)
+    {
+        if (specialWildcardCharacters != null)
+        {
+            for (int index = 0; index < specialWildcardCharacters.length; index++)
+            {
+                if (character == specialWildcardCharacters[index])
+                {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * A main method for testing string matching.
+     */
+    public static void main(String[] args)
+    {
+        try
+        {
+            System.out.println("Regular expression ["+args[0]+"]");
+            BasicMatcher matcher =
+                new BasicMatcher(args[0], null, new char[] {'/'}, null);
+
+            for (int index = 1; index < args.length; index++)
+            {
+                String string = args[index];
+                System.out.print("String             ["+string+"]");
+                System.out.println(" -> match = "+matcher.matches(args[index]));
+            }
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/src/proguard/util/ClassNameListMatcher.java b/src/proguard/util/ClassNameListMatcher.java
new file mode 100644
index 0000000..557883a
--- /dev/null
+++ b/src/proguard/util/ClassNameListMatcher.java
@@ -0,0 +1,88 @@
+/* $Id: ClassNameListMatcher.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.util;
+
+import java.util.List;
+
+
+/**
+ * This StringMatcher tests whether internal class names match any
+ * entry in a given list of regular expressions.
+ *
+ * @see BasicListMatcher
+ * @see ClassNameMatcher
+ *
+ * @author Eric Lafortune
+ */
+public class ClassNameListMatcher extends BasicListMatcher
+{
+    /**
+     * Creates a new ClassNameListMatcher.
+     * @param regularExpression the comma-separated list of regular expressions
+     *                          against which strings will be matched.
+     */
+    public ClassNameListMatcher(String regularExpression)
+    {
+        super(regularExpression);
+    }
+
+
+    /**
+     * Creates a new ClassNameListMatcher.
+     * @param regularExpressionList the list of regular expressions against which
+     *                              strings will be matched.
+     */
+    public ClassNameListMatcher(List regularExpressionList)
+    {
+        super(regularExpressionList);
+    }
+
+
+    // Overridden method of BasicListMatcher
+
+    protected StringMatcher createBasicMatcher(String regularExpression)
+    {
+        return new ClassNameMatcher(regularExpression);
+    }
+
+
+    /**
+     * A main method for testing file name matching.
+     */
+    public static void main(String[] args)
+    {
+        try
+        {
+            System.out.println("Regular expression ["+args[0]+"]");
+            ClassNameListMatcher matcher = new ClassNameListMatcher(args[0]);
+            for (int index = 1; index < args.length; index++)
+            {
+                String string = args[index];
+                System.out.print("String             ["+string+"]");
+                System.out.println(" -> match = "+matcher.matches(args[index]));
+            }
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/src/proguard/util/ClassNameMatcher.java b/src/proguard/util/ClassNameMatcher.java
new file mode 100644
index 0000000..f711300
--- /dev/null
+++ b/src/proguard/util/ClassNameMatcher.java
@@ -0,0 +1,93 @@
+/* $Id: ClassNameMatcher.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.util;
+
+import proguard.classfile.ClassConstants;
+
+
+/**
+ * This StringMatcher tests whether internal class names match a
+ * given regular expression.
+ * Supported wildcards are
+ * '?'  for a single Java identifier character,
+ * '*'  for any number of regular Java identifier characters, and
+ * '**' for any number of regular Java identifier characters or package separator
+ *      characters.
+ * '%'  for a single internal primitive type character (Z, B, C, S, I, F, J, or D),
+ *
+ * @author Eric Lafortune
+ */
+public class ClassNameMatcher extends BasicMatcher
+{
+    private static final char[] EXTENDED_CLASS_NAME_CHARACTERS = new char[]
+    {
+        ClassConstants.INTERNAL_PACKAGE_SEPARATOR
+    };
+
+    private static final char[] SPECIAL_PRIMITIVE_CHARACTERS = new char[]
+    {
+        ClassConstants.INTERNAL_TYPE_BOOLEAN,
+        ClassConstants.INTERNAL_TYPE_BYTE,
+        ClassConstants.INTERNAL_TYPE_CHAR,
+        ClassConstants.INTERNAL_TYPE_SHORT,
+        ClassConstants.INTERNAL_TYPE_INT,
+        ClassConstants.INTERNAL_TYPE_FLOAT,
+        ClassConstants.INTERNAL_TYPE_LONG,
+        ClassConstants.INTERNAL_TYPE_DOUBLE
+    };
+
+
+    /**
+     * Creates a new ClassNameMatcher.
+     * @param regularExpression the regular expression against which strings
+     *                          will be matched.
+     */
+    public ClassNameMatcher(String regularExpression)
+    {
+        super(regularExpression,
+              null,
+              EXTENDED_CLASS_NAME_CHARACTERS,
+              SPECIAL_PRIMITIVE_CHARACTERS);
+    }
+
+
+    /**
+     * A main method for testing class name matching.
+     */
+    public static void main(String[] args)
+    {
+        try
+        {
+            System.out.println("Regular expression ["+args[0]+"]");
+            ClassNameMatcher matcher = new ClassNameMatcher(args[0]);
+            for (int index = 1; index < args.length; index++)
+            {
+                String string = args[index];
+                System.out.print("String             ["+string+"]");
+                System.out.println(" -> match = "+matcher.matches(args[index]));
+            }
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/src/proguard/util/ExtensionMatcher.java b/src/proguard/util/ExtensionMatcher.java
new file mode 100644
index 0000000..84cc834
--- /dev/null
+++ b/src/proguard/util/ExtensionMatcher.java
@@ -0,0 +1,52 @@
+/* $Id: ExtensionMatcher.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.util;
+
+import java.util.*;
+
+
+/**
+ * This StringMatcher tests whether strings end in a given extension.
+ *
+ * @author Eric Lafortune
+ */
+public class ExtensionMatcher implements StringMatcher
+{
+    private String extension;
+
+
+    /**
+     * Creates a new StringMatcher.
+     * @param extension the extension against which strings will be matched.
+     */
+    public ExtensionMatcher(String extension)
+    {
+        this.extension = extension;
+    }
+
+
+    // Implementations for StringMatcher.
+
+    public boolean matches(String string)
+    {
+        return string.endsWith(extension);
+    }
+}
diff --git a/src/proguard/util/FileNameListMatcher.java b/src/proguard/util/FileNameListMatcher.java
new file mode 100644
index 0000000..765d100
--- /dev/null
+++ b/src/proguard/util/FileNameListMatcher.java
@@ -0,0 +1,88 @@
+/* $Id: FileNameListMatcher.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.util;
+
+import java.util.List;
+
+
+/**
+ * This StringMatcher tests whether file names match any entry in a
+ * given list of regular expressions.
+ *
+ * @see BasicListMatcher
+ * @see FileNameMatcher
+ *
+ * @author Eric Lafortune
+ */
+public class FileNameListMatcher extends BasicListMatcher
+{
+    /**
+     * Creates a new FileNameListMatcher.
+     * @param regularExpression the comma-separated list of regular expressions
+     *                          against which strings will be matched.
+     */
+    public FileNameListMatcher(String regularExpression)
+    {
+        super(regularExpression);
+    }
+
+
+    /**
+     * Creates a new FileNameListMatcher.
+     * @param regularExpressionList the list of regular expressions against which
+     *                              strings will be matched.
+     */
+    public FileNameListMatcher(List regularExpressionList)
+    {
+        super(regularExpressionList);
+    }
+
+
+    // Overridden method of BasicListMatcher
+
+    protected StringMatcher createBasicMatcher(String regularExpression)
+    {
+        return new FileNameMatcher(regularExpression);
+    }
+
+
+    /**
+     * A main method for testing file name matching.
+     */
+    public static void main(String[] args)
+    {
+        try
+        {
+            System.out.println("Regular expression ["+args[0]+"]");
+            FileNameListMatcher matcher = new FileNameListMatcher(args[0]);
+            for (int index = 1; index < args.length; index++)
+            {
+                String string = args[index];
+                System.out.print("String             ["+string+"]");
+                System.out.println(" -> match = "+matcher.matches(args[index]));
+            }
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/src/proguard/util/FileNameMatcher.java b/src/proguard/util/FileNameMatcher.java
new file mode 100644
index 0000000..719c71f
--- /dev/null
+++ b/src/proguard/util/FileNameMatcher.java
@@ -0,0 +1,89 @@
+/* $Id: FileNameMatcher.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.util;
+
+import proguard.classfile.ClassConstants;
+
+import java.io.*;
+
+
+/**
+ * This StringMatcher tests whether file names match a given regular
+ * expression.
+ * Supported wildcards are
+ * '?'  for a single regular file name character,
+ * '*'  for any number of regular file name characters, and
+ * '**' for any number of regular file name characters or directory separator
+ *      characters (always including '/').
+ *
+ * @author Eric Lafortune
+ */
+public class FileNameMatcher extends BasicMatcher
+{
+    private static final char[] FILE_NAME_CHARACTERS = new char[]
+    {
+        ' ',
+        '.'
+    };
+
+    private static final char[] EXTENDED_FILE_NAME_CHARACTERS = new char[]
+    {
+        ClassConstants.INTERNAL_PACKAGE_SEPARATOR,
+        File.separatorChar
+    };
+
+
+    /**
+     * Creates a new FileNameMatcher.
+     * @param regularExpression the regular expression against which strings
+     *                          will be matched.
+     */
+    public FileNameMatcher(String regularExpression)
+    {
+        super(regularExpression,
+              FILE_NAME_CHARACTERS,
+              EXTENDED_FILE_NAME_CHARACTERS,
+              null);
+    }
+
+
+    /**
+     * A main method for testing file name matching.
+     */
+    public static void main(String[] args)
+    {
+        try
+        {
+            System.out.println("Regular expression ["+args[0]+"]");
+            FileNameMatcher matcher = new FileNameMatcher(args[0]);
+            for (int index = 1; index < args.length; index++)
+            {
+                String string = args[index];
+                System.out.print("String             ["+string+"]");
+                System.out.println(" -> match = "+matcher.matches(args[index]));
+            }
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/src/proguard/util/ListUtil.java b/src/proguard/util/ListUtil.java
new file mode 100644
index 0000000..c0f7721
--- /dev/null
+++ b/src/proguard/util/ListUtil.java
@@ -0,0 +1,87 @@
+/* $Id: ListUtil.java,v 1.5 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.util;
+
+import java.util.*;
+
+
+/**
+ * This class provides some utility methods for working with
+ * <code>java.util.List</code> objects.
+ *
+ * @author Eric Lafortune
+ */
+public class ListUtil
+{
+    /**
+     * Creates a comma-separated String from the given List of String objects.
+     */
+    public static String commaSeparatedString(List list)
+    {
+        if (list == null)
+        {
+            return null;
+        }
+
+        StringBuffer buffer = new StringBuffer();
+
+        for (int index = 0; index < list.size(); index++)
+        {
+            if (index > 0)
+            {
+                buffer.append(',');
+            }
+
+            buffer.append(list.get(index));
+        }
+
+        return buffer.toString();
+    }
+
+
+    /**
+     * Creates a List of String objects from the given comma-separated String.
+     */
+    public static List commaSeparatedList(String string)
+    {
+        if (string == null)
+        {
+            return null;
+        }
+
+        List list = new ArrayList();
+        int index = 0;
+        while (index < string.length())
+        {
+            int nextIndex = string.indexOf(',', index);
+            if (nextIndex < 0)
+            {
+                nextIndex = string.length();
+            }
+
+            list.add(string.substring(index, nextIndex).trim());
+
+            index = nextIndex + 1;
+        }
+
+        return list;
+    }
+}
diff --git a/src/proguard/util/StringMatcher.java b/src/proguard/util/StringMatcher.java
new file mode 100644
index 0000000..92dd665
--- /dev/null
+++ b/src/proguard/util/StringMatcher.java
@@ -0,0 +1,38 @@
+/* $Id: StringMatcher.java,v 1.2 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.util;
+
+
+/**
+ * This interface provides a method to determine whether strings match a given
+ * criterion, which is specified by the implementation.
+ *
+ * @author Eric Lafortune
+ */
+public interface StringMatcher
+{
+    /**
+     * Checks whether the given string matches.
+     * @param string the string to match.
+     * @return a boolean indicating whether the string matches the criterion.
+     */
+    public boolean matches(String string);
+}
diff --git a/src/proguard/util/package.html b/src/proguard/util/package.html
new file mode 100644
index 0000000..cb14fdc
--- /dev/null
+++ b/src/proguard/util/package.html
@@ -0,0 +1,3 @@
+<body>
+This package contains utility classes for regular expression matching,...
+</body>
diff --git a/src/proguard/wtk/ProGuardObfuscator.java b/src/proguard/wtk/ProGuardObfuscator.java
new file mode 100644
index 0000000..c406805
--- /dev/null
+++ b/src/proguard/wtk/ProGuardObfuscator.java
@@ -0,0 +1,138 @@
+/* $Id: ProGuardObfuscator.java,v 1.12 2004/08/15 12:39:30 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2004 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.wtk;
+
+import com.sun.kvem.environment.*;
+import proguard.*;
+import proguard.classfile.*;
+
+import java.io.*;
+import java.util.*;
+
+
+/**
+ * ProGuard plug-in for the J2ME Wireless Toolkit.
+ * <p>
+ * In order to integrate this plug-in in the toolkit, you'll have to put the
+ * following lines in the file
+ * {j2mewtk.dir}<code>/wtklib/Linux/ktools.properties</code> or
+ * {j2mewtk.dir}<code>\wtklib\Windows\ktools.properties</code> (whichever is
+ * applicable).
+ * <p>
+ * <pre>
+ * obfuscator.runner.class.name: proguard.wtk.ProGuardObfuscator
+ * obfuscator.runner.classpath: /usr/local/java/proguard1.6/lib/proguard.jar
+ * </pre>
+ * Please make sure the class path is set correctly for your system.
+ *
+ * @author Eric Lafortune
+ */
+public class ProGuardObfuscator implements Obfuscator
+{
+    private static final String DEFAULT_CONFIGURATION = "default.pro";
+
+
+    // Implementations for Obfuscator.
+
+    public void createScriptFile(File jadFile,
+                                 File projectDir)
+    {
+        // We don't really need to create a script file;
+        // we'll just fill out all options in the run method.
+    }
+
+
+    public void run(File   obfuscatedJarFile,
+                    String wtkBinDir,
+                    String wtkLibDir,
+                    String jarFileName,
+                    String projectDirName,
+                    String classPath,
+                    String emptyAPI)
+    throws IOException
+    {
+        try
+        {
+            // Create the ProGuard configuration.
+            Configuration configuration = new Configuration();
+
+            // Parse the default configuration file.
+            ConfigurationParser parser = new ConfigurationParser(this.getClass().getResource(DEFAULT_CONFIGURATION));
+            parser.parse(configuration);
+
+            // Fill out the library class path.
+            configuration.libraryJars = classPath(classPath);
+
+            // Fill out the program class path (input and output).
+            configuration.programJars = new ClassPath();
+            configuration.programJars.add(new ClassPathEntry(jarFileName, false));
+            configuration.programJars.add(new ClassPathEntry(obfuscatedJarFile.getPath(), true));
+
+            // The preverify tool seems to unpack the resulting class files,
+            // so we must not use mixed-case class names on Windows.
+            configuration.useMixedCaseClassNames =
+                !System.getProperty("os.name").regionMatches(true, 0, "windows", 0, 7);
+
+            // Run ProGuard with these options.
+            ProGuard proGuard = new ProGuard(configuration);
+            proGuard.execute();
+
+        }
+        catch (ParseException ex)
+        {
+            throw new IOException(ex.getMessage());
+        }
+    }
+
+
+    /**
+     * Converts the given class path String into a ClassPath object.
+     */
+    private ClassPath classPath(String classPathString)
+    {
+        ClassPath classPath = new ClassPath();
+
+        String separator = System.getProperty("path.separator");
+
+        int index = 0;
+        while (index < classPathString.length())
+        {
+            // Find the next separator, or the end of the String.
+            int next_index = classPathString.indexOf(separator, index);
+            if (next_index < 0)
+            {
+                next_index = classPathString.length();
+            }
+
+            // Create and add the found class path entry.
+            ClassPathEntry classPathEntry =
+                new ClassPathEntry(classPathString.substring(index, next_index),
+                                   false);
+
+            classPath.add(classPathEntry);
+
+            // Continue after the separator.
+            index = next_index + 1;
+        }
+
+        return classPath;
+    }
+}
diff --git a/src/proguard/wtk/default.pro b/src/proguard/wtk/default.pro
new file mode 100644
index 0000000..d5e068c
--- /dev/null
+++ b/src/proguard/wtk/default.pro
@@ -0,0 +1,112 @@
+-dontnote
+-overloadaggressively
+-defaultpackage ''
+-allowaccessmodification
+
+# Keep all extensions of javax.microedition.midlet.MIDlet.
+-keep public class * extends javax.microedition.midlet.MIDlet
+
+# Keep all native class/method names.
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+# Remove all invocations of System methods without side effects
+# whose return values are not used.
+-assumenosideeffects public class java.lang.System {
+    public static native long currentTimeMillis();
+    static java.lang.Class getCallerClass();
+    public static native int identityHashCode(java.lang.Object);
+    public static java.lang.SecurityManager getSecurityManager();
+    public static java.util.Properties getProperties();
+    public static java.lang.String getProperty(java.lang.String);
+    public static java.lang.String getenv(java.lang.String);
+    public static native java.lang.String mapLibraryName(java.lang.String);
+    public static java.lang.String getProperty(java.lang.String,java.lang.String);
+}
+
+# Remove all invocations of String methods without side effects
+# whose return values are not used.
+-assumenosideeffects public class java.lang.String {
+    public java.lang.String();
+    public java.lang.String(byte[]);
+    public java.lang.String(byte[],int);
+    public java.lang.String(byte[],int,int);
+    public java.lang.String(byte[],int,int,int);
+    public java.lang.String(byte[],int,int,java.lang.String);
+    public java.lang.String(byte[],java.lang.String);
+    public java.lang.String(char[]);
+    public java.lang.String(char[],int,int);
+    public java.lang.String(java.lang.String);
+    public java.lang.String(java.lang.StringBuffer);
+    public static java.lang.String copyValueOf(char[]);
+    public static java.lang.String copyValueOf(char[],int,int);
+    public static java.lang.String valueOf(boolean);
+    public static java.lang.String valueOf(char);
+    public static java.lang.String valueOf(char[]);
+    public static java.lang.String valueOf(char[],int,int);
+    public static java.lang.String valueOf(double);
+    public static java.lang.String valueOf(float);
+    public static java.lang.String valueOf(int);
+    public static java.lang.String valueOf(java.lang.Object);
+    public static java.lang.String valueOf(long);
+    public boolean contentEquals(java.lang.StringBuffer);
+    public boolean endsWith(java.lang.String);
+    public boolean equalsIgnoreCase(java.lang.String);
+    public boolean equals(java.lang.Object);
+    public boolean matches(java.lang.String);
+    public boolean regionMatches(boolean,int,java.lang.String,int,int);
+    public boolean regionMatches(int,java.lang.String,int,int);
+    public boolean startsWith(java.lang.String);
+    public boolean startsWith(java.lang.String,int);
+    public byte[] getBytes();
+    public byte[] getBytes(java.lang.String);
+    public char charAt(int);
+    public char[] toCharArray();
+    public int compareToIgnoreCase(java.lang.String);
+    public int compareTo(java.lang.Object);
+    public int compareTo(java.lang.String);
+    public int hashCode();
+    public int indexOf(int);
+    public int indexOf(int,int);
+    public int indexOf(java.lang.String);
+    public int indexOf(java.lang.String,int);
+    public int lastIndexOf(int);
+    public int lastIndexOf(int,int);
+    public int lastIndexOf(java.lang.String);
+    public int lastIndexOf(java.lang.String,int);
+    public int length();
+    public java.lang.CharSequence subSequence(int,int);
+    public java.lang.String concat(java.lang.String);
+    public java.lang.String replaceAll(java.lang.String,java.lang.String);
+    public java.lang.String replace(char,char);
+    public java.lang.String replaceFirst(java.lang.String,java.lang.String);
+    public java.lang.String[] split(java.lang.String);
+    public java.lang.String[] split(java.lang.String,int);
+    public java.lang.String substring(int);
+    public java.lang.String substring(int,int);
+    public java.lang.String toLowerCase();
+    public java.lang.String toLowerCase(java.util.Locale);
+    public java.lang.String toString();
+    public java.lang.String toUpperCase();
+    public java.lang.String toUpperCase(java.util.Locale);
+    public java.lang.String trim();
+}
+
+
+# Remove all invocations of StringBuffer methods without side effects
+# whose return values are not used.
+-assumenosideeffects public class java.lang.StringBuffer {
+    public java.lang.StringBuffer();
+    public java.lang.StringBuffer(int);
+    public java.lang.StringBuffer(java.lang.String);
+    public java.lang.String toString();
+    public char charAt(int);
+    public int capacity();
+    public int indexOf(java.lang.String,int);
+    public int lastIndexOf(java.lang.String);
+    public int lastIndexOf(java.lang.String,int);
+    public int length();
+    public java.lang.String substring(int);
+    public java.lang.String substring(int,int);
+}
diff --git a/src/proguard/wtk/package.html b/src/proguard/wtk/package.html
new file mode 100644
index 0000000..6efc644
--- /dev/null
+++ b/src/proguard/wtk/package.html
@@ -0,0 +1,3 @@
+<body>
+This package contains the J2ME Wireless Toolkit plug-in for ProGuard.
+</body>

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/proguard.git



More information about the pkg-java-commits mailing list