1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package sfutils.frs;
29
30 import java.io.File;
31
32 import java.util.Arrays;
33 import java.util.Collection;
34 import java.util.Map;
35 import java.util.LinkedHashMap;
36 import java.util.Date;
37
38 import sfutils.Project;
39 import sfutils.SourceForgeException;
40
41 /***
42 * A {@link HideableNamedObject} corresponding to a <a
43 * href="http://sourceforge.net/">SourceForge</a> file release, the fundamental
44 * unit of publication at <a href="http://sourceforge.net/">SourceForge</a>.
45 *
46 * <p>Briefly:
47 *
48 * <ul>
49 *
50 * <li>a <a href="http://sourceforge.net/">SourceForge</a> <b>project</b> has
51 * one or more <b>packages</b></li>
52 *
53 * <li>a <a href="http://sourceforge.net/">SourceForge</a> package has zero or
54 * more <b>file releases</b></li>
55 *
56 * <li>a <a href="http://sourceforge.net/">SourceForge</a> file release has one
57 * or more <b>file specifications</b></li>
58 *
59 * </ul>
60 *
61 * These are represented by the following classes, in order:
62 *
63 * <ul>
64 *
65 * <li>{@link Project}</li>
66 *
67 * <li>{@link Package}</li>
68 *
69 * <li>{@link FileRelease}</li>
70 *
71 * <li>{@link FileSpecification}</li>
72 *
73 * </ul></p>
74 *
75 * @author <a href="mailto:ljnelson94@alumni.amherst.edu">Laird Nelson</a>
76 * @version $Revision: 1.14 $ $Date: 2003/08/07 21:42:23 $
77 * @since May 21, 2003
78 * @see Project
79 * @see Package
80 * @see FileRelease
81 * @see FileSpecification
82 * @see <a
83 * href="http://sourceforge.net/docman/display_doc.php?docid=6445&group_id=1">Guide
84 * to the SourceForge.net File Release System (FRS)</a>
85 * @see <a href="package-summary.html#package_description">The
86 * <code>sfutils.frs</code> package description</a>
87 */
88 public class FileRelease
89 extends HideableNamedObject implements FileSpecificationMap {
90
91 /***
92 * The identifier of this {@link FileRelease}. {@link FileRelease}
93 * identifiers are assigned by <a
94 * href="http://sourceforge.net/">SourceForge</a>. This field may be
95 * <code>null</code>.
96 */
97 private String id;
98
99 /***
100 * The {@link Package} this {@link FileRelease} is a part of. This field may
101 * be <code>null</code>.
102 */
103 private Package projectPackage;
104
105 /***
106 * The {@link Date} on which this {@link FileRelease} is to be released. This
107 * field may be <code>null</code>.
108 */
109 private Date releaseDate;
110
111 /***
112 * Whether or not to preserve any formatting that is present in either the
113 * {@link #releaseNotes} or {@link #changeLog} field. This field is
114 * initialized to <code>true</code>.
115 */
116 private boolean preserveFormattedText;
117
118 /***
119 * Whether or not to send email to those <a
120 * href="http://sourceforge.net/">SourceForge</a> users who are monitoring
121 * this {@link FileRelease}'s {@link Package} describing the new release.
122 * This field is initialized to <code>true</code>.
123 */
124 private boolean notifyOthers;
125
126 /***
127 * A {@link String} containing the release notes for this {@link FileRelease}.
128 * This field is ignored if the {@link #releaseNotesFile} field is
129 * non-<code>null</code>.
130 *
131 * <p>This field may be <code>null</code>.</p>
132 *
133 * @see #releaseNotesFile
134 */
135 private String releaseNotes;
136
137 /***
138 * A {@link File} containing the release notes for this {@link FileRelease}.
139 * The {@link #releaseNotes} field will be ignored if this field is
140 * non-<code>null</code>.
141 *
142 * @see #releaseNotes
143 */
144 private File releaseNotesFile;
145
146 /***
147 * A {@link String} containing the change log for this {@link FileRelease}.
148 * This field is ignored if the {@link #changeLogFile} field is
149 * non-<code>null</code>.
150 *
151 * <p>This field may be <code>null</code>.</p>
152 *
153 * @see #changeLogFile
154 */
155 private String changeLog;
156
157 /***
158 * A {@link File} containing the change log for this {@link FileRelease}. The
159 * {@link #changeLog} field will be ignored if this field is
160 * non-<code>null</code>.
161 *
162 * @see #releaseNotes
163 */
164 private File changeLogFile;
165
166 /***
167 * A {@link Map} of {@link FileSpecification}s indexed by the {@linkplain
168 * File#getName() unqualified name}s of their {@linkplain
169 * FileSpecification#getFile() associated <code>File</code>}s. This field
170 * cannot be <code>null</code>.
171 */
172 private final Map specs;
173
174 /***
175 * A {@link Publisher} that handles the uploading and distribution of this
176 * {@link FileRelease} to <a href="http://sourceforge.net/">SourceForge</a>.
177 * This field may be <code>null</code>.
178 */
179 private Publisher publisher;
180
181 /***
182 * Creates a new {@link FileRelease}. This constructor simply calls the
183 * {@link #FileRelease(Package, String)} constructor with <code>null</code>
184 * arguments.
185 *
186 * <p>The resulting {@link FileRelease} is in an illegal state until, at a
187 * minimum, its {@linkplain #setName(String) associated name} and {@linkplain
188 * #setPackage(Package) associated <code>Package</code>} are set.</p>
189 *
190 * @see #FileRelease(Package, String)
191 * @see #setName(String)
192 * @see #setPackage(Package)
193 */
194 public FileRelease() {
195 this(null, null);
196 }
197
198 /***
199 * Creates a new {@link FileRelease} and sets its {@linkplain #setName(String)
200 * associated name} to the supplied name. In addition, this constructor
201 * creates a new {@link Package} and {@linkplain Package#setName(String) sets
202 * its associated name} to the supplied name as well.
203 *
204 * <p>This constructor {@linkplain Package#Package(String) creates a new
205 * <code>Package</code> with the supplied name}, and passes it and the
206 * supplied name to the {@link #FileRelease(Package, String)} constructor.</p>
207 *
208 * @param name
209 * the name of this {@link FileRelease} (and the name that will
210 * be assigned to the new {@link Package} it will belong to);
211 * may be <code>null</code>, rather uselessly
212 * @see #FileRelease(Package, String)
213 * @see Package#Package(String)
214 */
215 public FileRelease(final String name) {
216 this(new Package(name), name);
217 }
218
219 /***
220 * Creates a new {@link FileRelease} that belongs to the supplied {@link
221 * Package}. This constructor simply calls the {@link #FileRelease(Package,
222 * String)} constructor, passing it the supplied {@link Package} and a
223 * <code>null</code> name.
224 *
225 * @param projectPackage
226 * the {@link Package} to which this {@link FileRelease}
227 * belongs; may be <code>null</code>, rather uselessly
228 * @see #FileRelease(Package, String)
229 */
230 public FileRelease(final Package projectPackage) {
231 this(projectPackage, null);
232 }
233
234 /***
235 * Creates a new {@link FileRelease} with the supplied name that belongs to
236 * the supplied {@link Package}. In addition, this constructor also
237 * {@linkplain #setReleaseDate(Date) sets the release date} to today's date,
238 * {@linkplain #setPreserveFormattedText(boolean) indicates that preformatted
239 * change log or release notes text is to be preserved}, and {@linkplain
240 * #setNotifyOthers(boolean) sets this <code>FileRelease</code> up to notify
241 * those monitoring it when it is released}.
242 *
243 * @param projectPackage
244 * the {@link Package} to which this {@link FileRelease}
245 * belongs; may be <code>null</code>, rather uselessly
246 * @param name
247 * the name of this new {@link FileRelease}; may be
248 * <code>null</code>, rather uselessly
249 * @see #setReleaseDate(Date)
250 * @see #setPreserveFormattedText(boolean)
251 * @see #setNotifyOthers(boolean)
252 */
253 public FileRelease(final Package projectPackage,
254 final String name) {
255 super(name);
256 this.specs = new LinkedHashMap(7);
257 this.setNotifyOthers(true);
258 this.setPackage(projectPackage);
259 this.setPreserveFormattedText(true);
260 this.setReleaseDate(new Date());
261 }
262
263 /***
264 * Sets the identifier of this {@link FileRelease}. Identifiers are assigned
265 * by <a href="http://sourceforge.net/">SourceForge</a>.
266 *
267 * @param id
268 * the identifier to set; should be a valid <a
269 * href="http://sourceforge.net/">SourceForge</a>-assigned file
270 * release identifier, but may be <code>null</code> (rather
271 * uselessly)
272 */
273 public void setID(final String id) {
274 this.id = id;
275 }
276
277 /***
278 * Returns the identifier of this {@link FileRelease}. This method may return
279 * <code>null</code>.
280 *
281 * @return the identifier of this {@link FileRelease}, or
282 * <code>null</code>
283 */
284 public String getID() {
285 return this.id;
286 }
287
288 /***
289 * Returns whether or not those monitoring this {@link FileRelease} will be
290 * notified when it is published.
291 *
292 * @return <code>true</code> if those monitoring this {@link FileRelease}
293 * will be notified when it is published; <code>false</code>
294 * otherwise
295 */
296 public boolean getNotifyOthers() {
297 return this.notifyOthers;
298 }
299
300 /***
301 * Sets whether or not those monitoring this {@link FileRelease} will be
302 * notified when it is published.
303 *
304 * @param notify
305 * if <code>true</code>, those monitoring this {@link
306 * FileRelease} will be notified when it is published
307 */
308 public void setNotifyOthers(final boolean notify) {
309 this.notifyOthers = notify;
310 }
311
312 /***
313 * Returns whether or not text present in the {@linkplain #getReleaseNotes()
314 * release notes} or {@linkplain #getChangeLog() change log} will have its
315 * formatting preserved.
316 *
317 * @return <code>true</code> if text present in the {@linkplain
318 * #getReleaseNotes() release notes} or {@linkplain
319 * #getChangeLog() change log} will have its formatting
320 * preserved; <code>false</code> otherwise
321 */
322 public boolean getPreserveFormattedText() {
323 return this.preserveFormattedText;
324 }
325
326 /***
327 * Sets whether or not text present in the {@linkplain #getReleaseNotes()
328 * release notes} or {@linkplain #getChangeLog() change log} will have its
329 * formatting preserved.
330 *
331 * @param preserve
332 * if <code>true</code>, text present in the {@linkplain
333 * #getReleaseNotes() release notes} or {@linkplain
334 * #getChangeLog() change log} will have its formatting
335 * preserved
336 */
337 public void setPreserveFormattedText(final boolean preserve) {
338 this.preserveFormattedText = preserve;
339 }
340
341 /***
342 * Returns the {@link Package} to which this {@link FileRelease} belongs.
343 * This method may return <code>null</code>.
344 *
345 * @return the {@link Package} to which this {@link FileRelease} belongs,
346 * or <code>null</code>
347 */
348 public Package getPackage() {
349 return this.projectPackage;
350 }
351
352 /***
353 * Sets the {@link Package} to which this {@link FileRelease} belongs.
354 *
355 * @param projectPackage
356 * the {@link Package} to which this {@link FileRelease}
357 * belongs; may be <code>null</code> (rather uselessly)
358 */
359 public void setPackage(final Package projectPackage) {
360 this.projectPackage = projectPackage;
361 }
362
363 /***
364 * Returns the {@link Date} on which this {@link FileRelease} is to be
365 * considered released. The returned {@link Date} may be <code>null</code>,
366 * and may not correspond to the actual date on which this {@link FileRelease}
367 * is released.
368 *
369 * @return the {@link Date} on which this {@link FileRelease} is to be
370 * considered released, or <code>null</code>
371 */
372 public Date getReleaseDate() {
373 return this.releaseDate;
374 }
375
376 /***
377 * Sets the {@link Date} on which this {@link FileRelease} is to be considered
378 * released.
379 *
380 * @param date
381 * the {@link Date} on which this {@link FileRelease} is to be
382 * considered released; may be <code>null</code>
383 */
384 public void setReleaseDate(final Date date) {
385 this.releaseDate = date;
386 }
387
388 /***
389 * Returns the release notes for this {@link FileRelease}. If this method
390 * returns <code>null</code>, make sure to check the return value of the
391 * {@link #getReleaseNotesFile()} method as well.
392 *
393 * @return the release notes for this {@link FileRelease}, or
394 * <code>null</code>
395 * @see #getReleaseNotesFile()
396 */
397 public String getReleaseNotes() {
398 return this.releaseNotes;
399 }
400
401 /***
402 * Sets the release notes for this {@link FileRelease}. Note that the
403 * contents of a {@linkplain #getReleaseNotesFile() release notes
404 * <code>File</code>} will be used in place of {@linkplain #getReleaseNotes()
405 * ad-hoc release notes} wherever possible.
406 *
407 * @param releaseNotes
408 * the release notes for this {@link FileRelease} ; may be
409 * <code>null</code> and will be ignored if the return value
410 * of the {@link #getReleaseNotesFile()} method is
411 * non-<code>null</code>
412 * @see #getReleaseNotesFile()
413 * @see #setReleaseNotesFile(File)
414 */
415 public void setReleaseNotes(final String releaseNotes)
416 throws IllegalArgumentException {
417 if (releaseNotes != null) {
418 final long lengthInBytes = releaseNotes.length() * 2;
419 if (lengthInBytes < 20L || lengthInBytes > 256000L) {
420 throw new IllegalArgumentException("SourceForge requires that release " +
421 "notes be between 20 and 256,000 " +
422 "bytes in length");
423 }
424 }
425 this.releaseNotes = releaseNotes;
426 }
427
428 /***
429 * Returns the {@link File} that contains the release notes for this {@link
430 * FileRelease}. This method may return <code>null</code>. If this method
431 * does <i>not</i> return <code>null</code>, then the returned {@link File}
432 * is guaranteed to be {@linkplain File#canRead() readable}.
433 *
434 * @return a {@linkplain File#canRead() readable} {@link File} that
435 * contains the release notes for this {@link FileRelease}, or
436 * <code>null</code>
437 */
438 public File getReleaseNotesFile() {
439 return this.releaseNotesFile;
440 }
441
442 /***
443 * Sets the {@link File} that contains release notes for this {@link
444 * FileRelease}. If the supplied {@link File} is non-<code>null</code>, then
445 * it must {@linkplain File#canRead() exist and be readable}.
446 *
447 * @param releaseNotesFile
448 * the {@link File} that contains release notes; may be
449 * <code>null</code>, but if <i>non</i>-<code>null</code>
450 * {@linkplain File#canRead() must exist and be readable}
451 * @exception IllegalArgumentException
452 * if the supplied {@link File} is not {@linkplain
453 * File#canRead() readable}
454 */
455 public void setReleaseNotesFile(final File releaseNotesFile)
456 throws IllegalArgumentException {
457 if (releaseNotesFile != null) {
458 if (!releaseNotesFile.canRead()) {
459 throw new IllegalArgumentException("release notes file must be readable");
460 } else {
461 final long length = releaseNotesFile.length();
462 if (length < 20L || length > 256000L) {
463 throw new IllegalArgumentException("SourceForge requires that release " +
464 "notes be between 20 and 256,000 " +
465 "bytes in length");
466 }
467 }
468 }
469 this.releaseNotesFile = releaseNotesFile;
470 }
471
472 /***
473 * Returns the change log for this {@link FileRelease}. If this method
474 * returns <code>null</code>, make sure to check the return value of the
475 * {@link #getChangeLogFile()} method as well.
476 *
477 * @return the change log for this {@link FileRelease}, or
478 * <code>null</code>
479 * @see #getChangeLogFile()
480 */
481 public String getChangeLog() {
482 return this.changeLog;
483 }
484
485 /***
486 * Sets the change log for this {@link FileRelease}. Note that the contents
487 * of a {@linkplain #getChangeLogFile() change log <code>File</code>} will be
488 * used in place of {@linkplain #getChangeLog() ad-hoc change log} wherever
489 * possible.
490 *
491 * @param changeLog
492 * the change log for this {@link FileRelease} ; may be
493 * <code>null</code> and will be ignored if the return value of
494 * the {@link #getChangeLogFile()} method is
495 * non-<code>null</code>
496 * @see #getChangeLogFile()
497 * @see #setChangeLogFile(File)
498 */
499 public void setChangeLog(final String changeLog) {
500 if (changeLog != null) {
501 final long lengthInBytes = changeLog.length() * 2;
502 if (lengthInBytes < 20L || lengthInBytes > 256000L) {
503 throw new IllegalArgumentException("SourceForge requires that " +
504 "changelog be between 20 and " +
505 "256,000 bytes in length");
506 }
507 }
508 this.changeLog = changeLog;
509 }
510
511 /***
512 * Returns the {@link File} that contains the change log for this {@link
513 * FileRelease}. This method may return <code>null</code>. If this method
514 * does <i>not</i> return <code>null</code>, then the returned {@link File}
515 * is guaranteed to be {@linkplain File#canRead() readable}.
516 *
517 * @return a {@linkplain File#canRead() readable} {@link File} that
518 * contains the change log for this {@link FileRelease}, or
519 * <code>null</code>
520 */
521 public File getChangeLogFile() {
522 return this.changeLogFile;
523 }
524
525 /***
526 * Sets the {@link File} that contains the change log for this {@link
527 * FileRelease}. If the supplied {@link File} is non-<code>null</code>, then
528 * it must {@linkplain File#canRead() exist and be readable}.
529 *
530 * @param changeLogFile
531 * the {@link File} that contains the change log; may be
532 * <code>null</code>, but if <i>non</i>-<code>null</code>
533 * {@linkplain File#canRead() must exist and be readable}
534 * @exception IllegalArgumentException
535 * if the supplied {@link File} is not {@linkplain
536 * File#canRead() readable}
537 */
538 public void setChangeLogFile(final File changeLogFile)
539 throws IllegalArgumentException {
540 if (changeLogFile != null) {
541 if (!changeLogFile.canRead()) {
542 throw new IllegalArgumentException("change log file must be readable");
543 } else {
544 final long length = changeLogFile.length();
545 if (length < 20L || length > 256000L) {
546 throw new IllegalArgumentException("SourceForge requires that " +
547 "changelogs be between 20 and " +
548 "256,000 bytes in length");
549 }
550 }
551 }
552 this.changeLogFile = changeLogFile;
553 }
554
555 /***
556 * A convenience method that returns all {@link File}s contained by the {@link
557 * #getFileSpecifications() FileSpecification}s associated with this {@link
558 * FileRelease}. This method's implementation iterates through this {@link
559 * FileRelease}'s {@linkplain #getFileSpecifications() associated
560 * <code>FileSpecification</code>s} and extracts their associated {@link
561 * File}s. This method may return <code>null</code>. Additionally, it cannot
562 * be guaranteed that there are not <code>null</code> elements inside the
563 * returned {@link File} array.
564 *
565 * @return all {@link File}s indirectly associated with this {@link
566 * FileRelease}, or <code>null</code>
567 */
568 public final File[] getFiles() {
569 final FileSpecification[] specs = this.getFileSpecifications();
570 if (specs == null) {
571 return null;
572 }
573 final File[] files = new File[specs.length];
574 FileSpecification spec;
575 for (int i = 0; i < specs.length; i++) {
576 spec = specs[i];
577 if (spec == null) {
578 files[i] = null;
579 } else {
580 files[i] = spec.getFile();
581 }
582 }
583 return files;
584 }
585
586 /***
587 * A convenience method that returns all {@linkplain File#getName() filenames}
588 * indirectly associated with this {@link FileRelease}. This method's
589 * implementation iterates through this {@link FileRelease}'s {@linkplain
590 * #getFiles() associated <code>File</code>s} and extract their {@linkplain
591 * File#getName() names}. This method may return <code>null</code>.
592 * Additionally, it cannot be guaranteed that there are not <code>null</code>
593 * elements inside the returned {@link File} array.
594 *
595 * @return all {@linkplain File#getName() filenames} indirectly associated
596 * with this {@link FileRelease}, or <code>null</code>
597 */
598 public final String[] getShortFileNames() {
599 final File[] files = this.getFiles();
600 if (files == null) {
601 return null;
602 }
603 final String[] names = new String[files.length];
604 File file;
605 for (int i = 0; i < files.length; i++) {
606 file = files[i];
607 if (file == null) {
608 names[i] = null;
609 } else {
610 names[i] = file.getName();
611 }
612 }
613 return names;
614 }
615
616 /***
617 * Returns all the {@link FileSpecification}s associated with this {@link
618 * FileRelease}. This method may return <code>null</code>.
619 *
620 * @return all the {@link FileSpecification}s associated with this {@link
621 * FileRelease}, or <code>null</code>
622 */
623 public FileSpecification[] getFileSpecifications() {
624 final Collection values = this.specs.values();
625 if (values == null) {
626 return null;
627 }
628 return (FileSpecification[])values.toArray(new FileSpecification[values.size()]);
629 }
630
631 /***
632 * Returns the {@link FileSpecification} that contains the {@link File} with
633 * the supplied {@linkplain File#getName() name}, or <code>null</code> if no
634 * such {@link FileSpecification} exists.
635 *
636 * @param fileBaseName
637 * the {@linkplain File#getName() short name} of the {@link
638 * File} in question; may be <code>null</code>
639 * @return the {@link FileSpecification} that contains the {@link File}
640 * with the supplied {@linkplain File#getName() name}, or
641 * <code>null</code>
642 */
643 public FileSpecification getFileSpecification(final String fileBaseName) {
644 return (FileSpecification)this.specs.get(fileBaseName);
645 }
646
647 /***
648 * Sets the {@link FileSpecification}s that are to be associated with this
649 * {@link FileRelease}.
650 *
651 * <p>If the supplied {@link FileSpecification} array is
652 * non-<code>null</code>, then no element of it may be <code>null</code> or an
653 * {@link IllegalArgumentException} will be thrown. In addition, any
654 * non-<code>null</code> {@link FileSpecification#getFile() File} associated
655 * with any given {@link FileSpecification} in the array must {@linkplain
656 * File#canRead() exist and be readable}.</p>
657 *
658 * @param specs
659 * the {@link FileSpecification}s; may be <code>null</code>
660 * @exception IllegalArgumentException
661 * if any of the {@link FileSpecification}s in the array is not
662 * set up properly; see this method's description for details
663 */
664 public void setFileSpecifications(final FileSpecification[] specs)
665 throws IllegalArgumentException {
666 if (specs != null) {
667 FileSpecification spec;
668 File file;
669 String name;
670 for (int i = 0; i < specs.length; i++) {
671 spec = specs[i];
672 if (spec == null) {
673 throw new IllegalArgumentException("specs contains null elements");
674 }
675 file = spec.getFile();
676 FileSpecification.validate(file);
677 name = file.getName();
678 assert name != null;
679 this.specs.put(name, spec);
680 }
681 }
682 }
683
684 /***
685 * Returns the {@link Publisher} used to upload this {@link FileRelease} to <a
686 * href="http://sourceforge.net/">SourceForge</a>. This method may return
687 * <code>null</code>.
688 *
689 * @return the {@link Publisher} used to upload this {@link FileRelease}
690 * to <a href="http://sourceforge.net/">SourceForge</a>, or
691 * <code>null</code>
692 */
693 public Publisher getPublisher() {
694 return this.publisher;
695 }
696
697 /***
698 * Installs the supplied {@link Publisher} as the {@link Publisher} that will
699 * be used to upload this {@link FileRelease} to <a
700 * href="http://sourceforge.net/">SourceForge</a>.
701 *
702 * @param publisher
703 * the new {@link Publisher}; may be <code>null</code>
704 */
705 public void setPublisher(final Publisher publisher) {
706 this.publisher = publisher;
707 }
708
709 /***
710 * Publishes this {@link FileRelease} to <a
711 * href="http://sourceforge.net/">SourceForge</a>. A non-<code>null</code>
712 * {@link Publisher} must {@linkplain #setPublisher(Publisher) have been
713 * previously installed}.
714 *
715 * @exception SourceForgeException
716 * if an error occurs
717 * @see Publisher#publish(FileRelease)
718 */
719 public void publish() throws SourceForgeException {
720 final Publisher publisher = this.getPublisher();
721 if (publisher == null) {
722 throw new PublishingException("Call setPublisher() first");
723 }
724 publisher.publish(this);
725 }
726
727 /***
728 * Returns a {@link String} representation of this {@link FileRelease}. This
729 * method never returns <code>null</code>.
730 *
731 * @return a {@link String} representation of this {@link FileRelease};
732 * never <code>null</code>
733 */
734 public String toString() {
735 final StringBuffer returnMe = new StringBuffer();
736 final String superRep = super.toString();
737 if (superRep != null) {
738 returnMe.append(superRep);
739 } else {
740 final String name = this.getName();
741 if (name != null) {
742 returnMe.append(name);
743 }
744 }
745 if (returnMe.length() > 0) {
746 returnMe.append(" ");
747 }
748 final FileSpecification[] specs = this.getFileSpecifications();
749 if (specs != null) {
750 returnMe.append(String.valueOf(Arrays.asList(specs)));
751 }
752 return returnMe.toString();
753 }
754
755 }