• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  *             of Java bytecode.
4  *
5  * Copyright (c) 2002-2014 Eric Lafortune (eric@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      */
ConfigurationWriter(File configurationFile)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      */
ConfigurationWriter(OutputStream outputStream)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      */
ConfigurationWriter(PrintWriter writer)73     public ConfigurationWriter(PrintWriter writer) throws IOException
74     {
75         this.writer = writer;
76     }
77 
78 
79     /**
80      * Closes this ConfigurationWriter.
81      */
close()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      */
write(Configuration configuration)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 
writeJarOptions(String inputEntryOptionName, String outputEntryOptionName, ClassPath classPath)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 
writeFilter(boolean filtered, List filter)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 
writeOption(String optionName, boolean flag)242     private void writeOption(String optionName, boolean flag)
243     {
244         if (flag)
245         {
246             writer.println(optionName);
247         }
248     }
249 
250 
writeOption(String optionName, int argument)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 
writeOption(String optionName, List arguments)262     private void writeOption(String optionName, List arguments)
263     {
264         writeOption(optionName, arguments, false);
265     }
266 
267 
writeOption(String optionName, List arguments, boolean replaceInternalClassNames)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 
writeOption(String optionName, String arguments)293     private void writeOption(String optionName, String arguments)
294     {
295         writeOption(optionName, arguments, false);
296     }
297 
298 
writeOption(String optionName, String arguments, boolean replaceInternalClassNames)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 
writeOption(String optionName, File file)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 
writeOptions(String[] optionNames, List keepClassSpecifications)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 
writeOption(String[] optionNames, KeepClassSpecification keepClassSpecification)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 
writeOptions(String optionName, List classSpecifications)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 
writeOption(String optionName, ClassSpecification classSpecification)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 
writeComments(String comments)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 
writeFieldSpecification(List memberSpecifications)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 
writeMethodSpecification(List memberSpecifications)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      */
externalClassNames(List internalClassNames)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      */
relativeFileName(File file)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      */
quotedString(String string)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      */
main(String[] args)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