Home | History | Annotate | Download | only in proguard
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2014 Eric Lafortune (eric (at) graphics.cornell.edu)
      6  *
      7  * This program is free software; you can redistribute it and/or modify it
      8  * under the terms of the GNU General Public License as published by the Free
      9  * Software Foundation; either version 2 of the License, or (at your option)
     10  * any later version.
     11  *
     12  * This program is distributed in the hope that it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
     15  * more details.
     16  *
     17  * You should have received a copy of the GNU General Public License along
     18  * with this program; if not, write to the Free Software Foundation, Inc.,
     19  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
     20  */
     21 package proguard;
     22 
     23 import proguard.classfile.ClassConstants;
     24 import proguard.classfile.util.ClassUtil;
     25 import proguard.util.ListUtil;
     26 
     27 import java.io.*;
     28 import java.util.*;
     29 
     30 
     31 /**
     32  * This class writes ProGuard configurations to a file.
     33  *
     34  * @author Eric Lafortune
     35  */
     36 public class ConfigurationWriter
     37 {
     38     private static final String[] KEEP_OPTIONS = new String[]
     39     {
     40         ConfigurationConstants.KEEP_OPTION,
     41         ConfigurationConstants.KEEP_CLASS_MEMBERS_OPTION,
     42         ConfigurationConstants.KEEP_CLASSES_WITH_MEMBERS_OPTION
     43     };
     44 
     45 
     46     private final PrintWriter writer;
     47     private       File        baseDir;
     48 
     49 
     50     /**
     51      * Creates a new ConfigurationWriter for the given file name.
     52      */
     53     public ConfigurationWriter(File configurationFile) throws IOException
     54     {
     55         this(new PrintWriter(new FileWriter(configurationFile)));
     56 
     57         baseDir = configurationFile.getParentFile();
     58     }
     59 
     60 
     61     /**
     62      * Creates a new ConfigurationWriter for the given OutputStream.
     63      */
     64     public ConfigurationWriter(OutputStream outputStream) throws IOException
     65     {
     66         this(new PrintWriter(outputStream));
     67     }
     68 
     69 
     70     /**
     71      * Creates a new ConfigurationWriter for the given PrintWriter.
     72      */
     73     public ConfigurationWriter(PrintWriter writer) throws IOException
     74     {
     75         this.writer = writer;
     76     }
     77 
     78 
     79     /**
     80      * Closes this ConfigurationWriter.
     81      */
     82     public void close() throws IOException
     83     {
     84         writer.close();
     85     }
     86 
     87 
     88     /**
     89      * Writes the given configuration.
     90      * @param configuration the configuration that is to be written out.
     91      * @throws IOException if an IO error occurs while writing the configuration.
     92      */
     93     public void write(Configuration configuration) throws IOException
     94     {
     95         // Write the program class path (input and output entries).
     96         writeJarOptions(ConfigurationConstants.INJARS_OPTION,
     97                         ConfigurationConstants.OUTJARS_OPTION,
     98                         configuration.programJars);
     99         writer.println();
    100 
    101         // Write the library class path (output entries only).
    102         writeJarOptions(ConfigurationConstants.LIBRARYJARS_OPTION,
    103                         ConfigurationConstants.LIBRARYJARS_OPTION,
    104                         configuration.libraryJars);
    105         writer.println();
    106 
    107         // Android-added: Write value of -systemjars option to configuration file.
    108         // Write the system class path (output entries only).
    109         writeJarOptions(ConfigurationConstants.SYSTEMJARS_OPTION,
    110                         ConfigurationConstants.SYSTEMJARS_OPTION,
    111                         configuration.systemJars);
    112         writer.println();
    113 
    114         // Write the other options.
    115         writeOption(ConfigurationConstants.SKIP_NON_PUBLIC_LIBRARY_CLASSES_OPTION,            configuration.skipNonPublicLibraryClasses);
    116         writeOption(ConfigurationConstants.DONT_SKIP_NON_PUBLIC_LIBRARY_CLASS_MEMBERS_OPTION, !configuration.skipNonPublicLibraryClassMembers);
    117         writeOption(ConfigurationConstants.KEEP_DIRECTORIES_OPTION,                           configuration.keepDirectories);
    118         writeOption(ConfigurationConstants.TARGET_OPTION,                                     ClassUtil.externalClassVersion(configuration.targetClassVersion));
    119         writeOption(ConfigurationConstants.FORCE_PROCESSING_OPTION,                           configuration.lastModified == Long.MAX_VALUE);
    120 
    121         writeOption(ConfigurationConstants.DONT_SHRINK_OPTION, !configuration.shrink);
    122         writeOption(ConfigurationConstants.PRINT_USAGE_OPTION, configuration.printUsage);
    123 
    124         writeOption(ConfigurationConstants.DONT_OPTIMIZE_OPTION,                 !configuration.optimize);
    125         writeOption(ConfigurationConstants.OPTIMIZATIONS,                        configuration.optimizations);
    126         writeOption(ConfigurationConstants.OPTIMIZATION_PASSES,                  configuration.optimizationPasses);
    127         writeOption(ConfigurationConstants.ALLOW_ACCESS_MODIFICATION_OPTION,     configuration.allowAccessModification);
    128         writeOption(ConfigurationConstants.MERGE_INTERFACES_AGGRESSIVELY_OPTION, configuration.mergeInterfacesAggressively);
    129 
    130         writeOption(ConfigurationConstants.DONT_OBFUSCATE_OPTION,                  !configuration.obfuscate);
    131         writeOption(ConfigurationConstants.PRINT_MAPPING_OPTION,                   configuration.printMapping);
    132         writeOption(ConfigurationConstants.APPLY_MAPPING_OPTION,                   configuration.applyMapping);
    133         writeOption(ConfigurationConstants.OBFUSCATION_DICTIONARY_OPTION,          configuration.obfuscationDictionary);
    134         writeOption(ConfigurationConstants.CLASS_OBFUSCATION_DICTIONARY_OPTION,    configuration.classObfuscationDictionary);
    135         writeOption(ConfigurationConstants.PACKAGE_OBFUSCATION_DICTIONARY_OPTION,  configuration.packageObfuscationDictionary);
    136         writeOption(ConfigurationConstants.OVERLOAD_AGGRESSIVELY_OPTION,           configuration.overloadAggressively);
    137         writeOption(ConfigurationConstants.USE_UNIQUE_CLASS_MEMBER_NAMES_OPTION,   configuration.useUniqueClassMemberNames);
    138         writeOption(ConfigurationConstants.DONT_USE_MIXED_CASE_CLASS_NAMES_OPTION, !configuration.useMixedCaseClassNames);
    139         writeOption(ConfigurationConstants.KEEP_PACKAGE_NAMES_OPTION,              configuration.keepPackageNames, true);
    140         writeOption(ConfigurationConstants.FLATTEN_PACKAGE_HIERARCHY_OPTION,       configuration.flattenPackageHierarchy, true);
    141         writeOption(ConfigurationConstants.REPACKAGE_CLASSES_OPTION,               configuration.repackageClasses, true);
    142         writeOption(ConfigurationConstants.KEEP_ATTRIBUTES_OPTION,                 configuration.keepAttributes);
    143         writeOption(ConfigurationConstants.KEEP_PARAMETER_NAMES_OPTION,            configuration.keepParameterNames);
    144         writeOption(ConfigurationConstants.RENAME_SOURCE_FILE_ATTRIBUTE_OPTION,    configuration.newSourceFileAttribute);
    145         writeOption(ConfigurationConstants.ADAPT_CLASS_STRINGS_OPTION,             configuration.adaptClassStrings, true);
    146         writeOption(ConfigurationConstants.ADAPT_RESOURCE_FILE_NAMES_OPTION,       configuration.adaptResourceFileNames);
    147         writeOption(ConfigurationConstants.ADAPT_RESOURCE_FILE_CONTENTS_OPTION,    configuration.adaptResourceFileContents);
    148 
    149         writeOption(ConfigurationConstants.DONT_PREVERIFY_OPTION, !configuration.preverify);
    150         writeOption(ConfigurationConstants.MICRO_EDITION_OPTION,  configuration.microEdition);
    151 
    152         writeOption(ConfigurationConstants.VERBOSE_OPTION,             configuration.verbose);
    153         writeOption(ConfigurationConstants.DONT_NOTE_OPTION,           configuration.note, true);
    154         writeOption(ConfigurationConstants.DONT_WARN_OPTION,           configuration.warn, true);
    155         writeOption(ConfigurationConstants.IGNORE_WARNINGS_OPTION,     configuration.ignoreWarnings);
    156         writeOption(ConfigurationConstants.PRINT_CONFIGURATION_OPTION, configuration.printConfiguration);
    157         writeOption(ConfigurationConstants.DUMP_OPTION,                configuration.dump);
    158 
    159         writeOption(ConfigurationConstants.PRINT_SEEDS_OPTION,     configuration.printSeeds);
    160         writer.println();
    161 
    162         // Write the "why are you keeping" options.
    163         writeOptions(ConfigurationConstants.WHY_ARE_YOU_KEEPING_OPTION, configuration.whyAreYouKeeping);
    164 
    165         // Write the keep options.
    166         writeOptions(KEEP_OPTIONS, configuration.keep);
    167 
    168         // Write the "no side effect methods" options.
    169         writeOptions(ConfigurationConstants.ASSUME_NO_SIDE_EFFECTS_OPTION, configuration.assumeNoSideEffects);
    170 
    171         if (writer.checkError())
    172         {
    173             throw new IOException("Can't write configuration");
    174         }
    175     }
    176 
    177 
    178     private void writeJarOptions(String    inputEntryOptionName,
    179                                  String    outputEntryOptionName,
    180                                  ClassPath classPath)
    181     {
    182         if (classPath != null)
    183         {
    184             for (int index = 0; index < classPath.size(); index++)
    185             {
    186                 ClassPathEntry entry = classPath.get(index);
    187                 String optionName = entry.isOutput() ?
    188                      outputEntryOptionName :
    189                      inputEntryOptionName;
    190 
    191                 writer.print(optionName);
    192                 writer.print(' ');
    193                 writer.print(relativeFileName(entry.getFile()));
    194 
    195                 // Append the filters, if any.
    196                 boolean filtered = false;
    197 
    198                 // For backward compatibility, the aar and apk filters come
    199                 // first.
    200                 filtered = writeFilter(filtered, entry.getAarFilter());
    201                 filtered = writeFilter(filtered, entry.getApkFilter());
    202                 filtered = writeFilter(filtered, entry.getZipFilter());
    203                 filtered = writeFilter(filtered, entry.getEarFilter());
    204                 filtered = writeFilter(filtered, entry.getWarFilter());
    205                 filtered = writeFilter(filtered, entry.getJarFilter());
    206                 filtered = writeFilter(filtered, entry.getFilter());
    207 
    208                 if (filtered)
    209                 {
    210                     writer.print(ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD);
    211                 }
    212 
    213                 writer.println();
    214             }
    215         }
    216     }
    217 
    218 
    219     private boolean writeFilter(boolean filtered, List filter)
    220     {
    221         if (filtered)
    222         {
    223             writer.print(ConfigurationConstants.SEPARATOR_KEYWORD);
    224         }
    225 
    226         if (filter != null)
    227         {
    228             if (!filtered)
    229             {
    230                 writer.print(ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD);
    231             }
    232 
    233             writer.print(ListUtil.commaSeparatedString(filter, true));
    234 
    235             filtered = true;
    236         }
    237 
    238         return filtered;
    239     }
    240 
    241 
    242     private void writeOption(String optionName, boolean flag)
    243     {
    244         if (flag)
    245         {
    246             writer.println(optionName);
    247         }
    248     }
    249 
    250 
    251     private void writeOption(String optionName, int argument)
    252     {
    253         if (argument != 1)
    254         {
    255             writer.print(optionName);
    256             writer.print(' ');
    257             writer.println(argument);
    258         }
    259     }
    260 
    261 
    262     private void writeOption(String optionName, List arguments)
    263     {
    264         writeOption(optionName, arguments, false);
    265     }
    266 
    267 
    268     private void writeOption(String  optionName,
    269                              List    arguments,
    270                              boolean replaceInternalClassNames)
    271     {
    272         if (arguments != null)
    273         {
    274             if (arguments.isEmpty())
    275             {
    276                 writer.println(optionName);
    277             }
    278             else
    279             {
    280                 if (replaceInternalClassNames)
    281                 {
    282                     arguments = externalClassNames(arguments);
    283                 }
    284 
    285                 writer.print(optionName);
    286                 writer.print(' ');
    287                 writer.println(ListUtil.commaSeparatedString(arguments, true));
    288             }
    289         }
    290     }
    291 
    292 
    293     private void writeOption(String optionName, String arguments)
    294     {
    295         writeOption(optionName, arguments, false);
    296     }
    297 
    298 
    299     private void writeOption(String  optionName,
    300                              String  arguments,
    301                              boolean replaceInternalClassNames)
    302     {
    303         if (arguments != null)
    304         {
    305             if (replaceInternalClassNames)
    306             {
    307                 arguments = ClassUtil.externalClassName(arguments);
    308             }
    309 
    310             writer.print(optionName);
    311             writer.print(' ');
    312             writer.println(quotedString(arguments));
    313         }
    314     }
    315 
    316 
    317     private void writeOption(String optionName, File file)
    318     {
    319         if (file != null)
    320         {
    321             if (file.getPath().length() > 0)
    322             {
    323                 writer.print(optionName);
    324                 writer.print(' ');
    325                 writer.println(relativeFileName(file));
    326             }
    327             else
    328             {
    329                 writer.println(optionName);
    330             }
    331         }
    332     }
    333 
    334 
    335     private void writeOptions(String[] optionNames,
    336                               List     keepClassSpecifications)
    337     {
    338         if (keepClassSpecifications != null)
    339         {
    340             for (int index = 0; index < keepClassSpecifications.size(); index++)
    341             {
    342                 writeOption(optionNames, (KeepClassSpecification)keepClassSpecifications.get(index));
    343             }
    344         }
    345     }
    346 
    347 
    348     private void writeOption(String[]               optionNames,
    349                              KeepClassSpecification keepClassSpecification)
    350     {
    351         // Compose the option name.
    352         String optionName = optionNames[keepClassSpecification.markConditionally ? 2 :
    353                                         keepClassSpecification.markClasses       ? 0 :
    354                                                                                    1];
    355 
    356         if (keepClassSpecification.markDescriptorClasses)
    357         {
    358             optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD +
    359                           ConfigurationConstants.INCLUDE_DESCRIPTOR_CLASSES_SUBOPTION;
    360         }
    361 
    362         if (keepClassSpecification.allowShrinking)
    363         {
    364             optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD +
    365                           ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION;
    366         }
    367 
    368         if (keepClassSpecification.allowOptimization)
    369         {
    370             optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD +
    371                           ConfigurationConstants.ALLOW_OPTIMIZATION_SUBOPTION;
    372         }
    373 
    374         if (keepClassSpecification.allowObfuscation)
    375         {
    376             optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD +
    377                           ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION;
    378         }
    379 
    380         // Write out the option with the proper class specification.
    381         writeOption(optionName, keepClassSpecification);
    382     }
    383 
    384 
    385     private void writeOptions(String optionName,
    386                               List   classSpecifications)
    387     {
    388         if (classSpecifications != null)
    389         {
    390             for (int index = 0; index < classSpecifications.size(); index++)
    391             {
    392                 writeOption(optionName, (ClassSpecification)classSpecifications.get(index));
    393             }
    394         }
    395     }
    396 
    397 
    398     private void writeOption(String             optionName,
    399                              ClassSpecification classSpecification)
    400     {
    401         writer.println();
    402 
    403         // Write out the comments for this option.
    404         writeComments(classSpecification.comments);
    405 
    406         writer.print(optionName);
    407         writer.print(' ');
    408 
    409         // Write out the required annotation, if any.
    410         if (classSpecification.annotationType != null)
    411         {
    412             writer.print(ConfigurationConstants.ANNOTATION_KEYWORD);
    413             writer.print(ClassUtil.externalType(classSpecification.annotationType));
    414             writer.print(' ');
    415         }
    416 
    417         // Write out the class access flags.
    418         writer.print(ClassUtil.externalClassAccessFlags(classSpecification.requiredUnsetAccessFlags,
    419                                                         ConfigurationConstants.NEGATOR_KEYWORD));
    420 
    421         writer.print(ClassUtil.externalClassAccessFlags(classSpecification.requiredSetAccessFlags));
    422 
    423         // Write out the class keyword, if we didn't write the interface
    424         // keyword earlier.
    425         if (((classSpecification.requiredSetAccessFlags |
    426               classSpecification.requiredUnsetAccessFlags) &
    427              (ClassConstants.ACC_INTERFACE |
    428               ClassConstants.ACC_ENUM)) == 0)
    429         {
    430             writer.print(ConfigurationConstants.CLASS_KEYWORD);
    431         }
    432 
    433         writer.print(' ');
    434 
    435         // Write out the class name.
    436         writer.print(classSpecification.className != null ?
    437             ClassUtil.externalClassName(classSpecification.className) :
    438             ConfigurationConstants.ANY_CLASS_KEYWORD);
    439 
    440         // Write out the extends template, if any.
    441         if (classSpecification.extendsAnnotationType != null ||
    442             classSpecification.extendsClassName      != null)
    443         {
    444             writer.print(' ');
    445             writer.print(ConfigurationConstants.EXTENDS_KEYWORD);
    446             writer.print(' ');
    447 
    448             // Write out the required extends annotation, if any.
    449             if (classSpecification.extendsAnnotationType != null)
    450             {
    451                 writer.print(ConfigurationConstants.ANNOTATION_KEYWORD);
    452                 writer.print(ClassUtil.externalType(classSpecification.extendsAnnotationType));
    453                 writer.print(' ');
    454             }
    455 
    456             // Write out the extended class name.
    457             writer.print(classSpecification.extendsClassName != null ?
    458                 ClassUtil.externalClassName(classSpecification.extendsClassName) :
    459                 ConfigurationConstants.ANY_CLASS_KEYWORD);
    460         }
    461 
    462         // Write out the keep field and keep method options, if any.
    463         if (classSpecification.fieldSpecifications  != null ||
    464             classSpecification.methodSpecifications != null)
    465         {
    466             writer.print(' ');
    467             writer.println(ConfigurationConstants.OPEN_KEYWORD);
    468 
    469             writeFieldSpecification( classSpecification.fieldSpecifications);
    470             writeMethodSpecification(classSpecification.methodSpecifications);
    471 
    472             writer.println(ConfigurationConstants.CLOSE_KEYWORD);
    473         }
    474         else
    475         {
    476             writer.println();
    477         }
    478     }
    479 
    480 
    481 
    482     private void writeComments(String comments)
    483     {
    484         if (comments != null)
    485         {
    486             int index = 0;
    487             while (index < comments.length())
    488             {
    489                 int breakIndex = comments.indexOf('\n', index);
    490                 if (breakIndex < 0)
    491                 {
    492                     breakIndex = comments.length();
    493                 }
    494 
    495                 writer.print('#');
    496 
    497                 if (comments.charAt(index) != ' ')
    498                 {
    499                     writer.print(' ');
    500                 }
    501 
    502                 writer.println(comments.substring(index, breakIndex));
    503 
    504                 index = breakIndex + 1;
    505             }
    506         }
    507     }
    508 
    509 
    510     private void writeFieldSpecification(List memberSpecifications)
    511     {
    512         if (memberSpecifications != null)
    513         {
    514             for (int index = 0; index < memberSpecifications.size(); index++)
    515             {
    516                 MemberSpecification memberSpecification =
    517                     (MemberSpecification)memberSpecifications.get(index);
    518 
    519                 writer.print("    ");
    520 
    521                 // Write out the required annotation, if any.
    522                 if (memberSpecification.annotationType != null)
    523                 {
    524                     writer.print(ConfigurationConstants.ANNOTATION_KEYWORD);
    525                     writer.println(ClassUtil.externalType(memberSpecification.annotationType));
    526                     writer.print("    ");
    527                 }
    528 
    529                 // Write out the field access flags.
    530                 writer.print(ClassUtil.externalFieldAccessFlags(memberSpecification.requiredUnsetAccessFlags,
    531                                                                 ConfigurationConstants.NEGATOR_KEYWORD));
    532 
    533                 writer.print(ClassUtil.externalFieldAccessFlags(memberSpecification.requiredSetAccessFlags));
    534 
    535                 // Write out the field name and descriptor.
    536                 String name       = memberSpecification.name;
    537                 String descriptor = memberSpecification.descriptor;
    538 
    539                 writer.print(descriptor == null ? name == null ?
    540                     ConfigurationConstants.ANY_FIELD_KEYWORD             :
    541                     ConfigurationConstants.ANY_TYPE_KEYWORD + ' ' + name :
    542                     ClassUtil.externalFullFieldDescription(0,
    543                                                            name == null ? ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD : name,
    544                                                            descriptor));
    545 
    546                 writer.println(ConfigurationConstants.SEPARATOR_KEYWORD);
    547             }
    548         }
    549     }
    550 
    551 
    552     private void writeMethodSpecification(List memberSpecifications)
    553     {
    554         if (memberSpecifications != null)
    555         {
    556             for (int index = 0; index < memberSpecifications.size(); index++)
    557             {
    558                 MemberSpecification memberSpecification =
    559                     (MemberSpecification)memberSpecifications.get(index);
    560 
    561                 writer.print("    ");
    562 
    563                 // Write out the required annotation, if any.
    564                 if (memberSpecification.annotationType != null)
    565                 {
    566                     writer.print(ConfigurationConstants.ANNOTATION_KEYWORD);
    567                     writer.println(ClassUtil.externalType(memberSpecification.annotationType));
    568                     writer.print("    ");
    569                 }
    570 
    571                 // Write out the method access flags.
    572                 writer.print(ClassUtil.externalMethodAccessFlags(memberSpecification.requiredUnsetAccessFlags,
    573                                                                  ConfigurationConstants.NEGATOR_KEYWORD));
    574 
    575                 writer.print(ClassUtil.externalMethodAccessFlags(memberSpecification.requiredSetAccessFlags));
    576 
    577                 // Write out the method name and descriptor.
    578                 String name       = memberSpecification.name;
    579                 String descriptor = memberSpecification.descriptor;
    580 
    581                 writer.print(descriptor == null ? name == null ?
    582                     ConfigurationConstants.ANY_METHOD_KEYWORD :
    583                     ConfigurationConstants.ANY_TYPE_KEYWORD + ' ' + name + ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD + ConfigurationConstants.ANY_ARGUMENTS_KEYWORD + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD :
    584                     ClassUtil.externalFullMethodDescription(ClassConstants.METHOD_NAME_INIT,
    585                                                             0,
    586                                                             name == null ? ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD : name,
    587                                                             descriptor));
    588 
    589                 writer.println(ConfigurationConstants.SEPARATOR_KEYWORD);
    590             }
    591         }
    592     }
    593 
    594 
    595     /**
    596      * Returns a list with external versions of the given list of internal
    597      * class names.
    598      */
    599     private List externalClassNames(List internalClassNames)
    600     {
    601         List externalClassNames = new ArrayList(internalClassNames.size());
    602 
    603         for (int index = 0; index < internalClassNames.size(); index++)
    604         {
    605             externalClassNames.add(ClassUtil.externalClassName((String)internalClassNames.get(index)));
    606         }
    607 
    608         return externalClassNames;
    609     }
    610 
    611 
    612     /**
    613      * Returns a relative file name of the given file, if possible.
    614      * The file name is also quoted, if necessary.
    615      */
    616     private String relativeFileName(File file)
    617     {
    618         String fileName = file.getAbsolutePath();
    619 
    620         // See if we can convert the file name into a relative file name.
    621         if (baseDir != null)
    622         {
    623             String baseDirName = baseDir.getAbsolutePath() + File.separator;
    624             if (fileName.startsWith(baseDirName))
    625             {
    626                 fileName = fileName.substring(baseDirName.length());
    627             }
    628         }
    629 
    630         return quotedString(fileName);
    631     }
    632 
    633 
    634     /**
    635      * Returns a quoted version of the given string, if necessary.
    636      */
    637     private String quotedString(String string)
    638     {
    639         return string.length()     == 0 ||
    640                string.indexOf(' ') >= 0 ||
    641                string.indexOf('@') >= 0 ||
    642                string.indexOf('{') >= 0 ||
    643                string.indexOf('}') >= 0 ||
    644                string.indexOf('(') >= 0 ||
    645                string.indexOf(')') >= 0 ||
    646                string.indexOf(':') >= 0 ||
    647                string.indexOf(';') >= 0 ||
    648                string.indexOf(',') >= 0  ? ("'" + string + "'") :
    649                                            (      string      );
    650     }
    651 
    652 
    653     /**
    654      * A main method for testing configuration writing.
    655      */
    656     public static void main(String[] args) {
    657         try
    658         {
    659             ConfigurationWriter writer = new ConfigurationWriter(new File(args[0]));
    660 
    661             writer.write(new Configuration());
    662         }
    663         catch (Exception ex)
    664         {
    665             ex.printStackTrace();
    666         }
    667     }
    668 }
    669