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 import java.io.Serializable;
32
33 import java.lang.reflect.Field;
34 import java.lang.reflect.Modifier;
35
36 import java.util.Collections;
37 import java.util.Date;
38 import java.util.HashMap;
39 import java.util.Map;
40
41 import sfutils.Project;
42
43 /***
44 * A collection of <a href="http://sourceforge.net/">SourceForge</a>-specific
45 * attributes to be associated with a {@link File} in a {@link FileRelease}.
46 *
47 * <p><a name="fileReqs"></a><a href="http://sourceforge.net/">SourceForge</a>
48 * imposes a number of requirements on the name and contents of a {@link File}.
49 * Specifically, a {@link File} must <strong>not</strong>:
50 *
51 * <ul>
52 *
53 * <li>be <code>null</code></li>
54 *
55 * <li>have a {@linkplain File#getName() filename} that contains either a space
56 * (" "), tilde ("~"), left parenthesis ("(") or right parenthesis
57 * (")")</li>
58 *
59 * <li>be fewer than 20 bytes in {@linkplain File#length() length}</li>
60 *
61 * <li>be greater than 256,000 bytes in {@linkplain File#length()}</li>
62 *
63 * </ul>
64 *
65 * Additionally, a {@link FileSpecification}'s {@linkplain #getFile() associated
66 * <code>File</code>} must {@linkplain File#exists() exist} and {@linkplain
67 * File#canRead() be readable}.</p>
68 *
69 * @author <a href="mailto:ljnelson94@alumni.amherst.edu">Laird Nelson</a>
70 * @version $Revision: 1.10 $ $Date: 2003/07/13 02:47:02 $
71 * @since June 19, 2003
72 * @see Project
73 * @see Package
74 * @see FileRelease
75 * @see FileSpecification
76 * @see <a
77 * href="http://sourceforge.net/docman/display_doc.php?docid=6445&group_id=1">Guide
78 * to the SourceForge.net File Release System (FRS)</a>
79 */
80 public class FileSpecification implements Serializable {
81
82 /***
83 * A constant for use by the {@link #setFileType(int)} method; indicates that
84 * the file is a <a
85 * href="http://www.debian.org/doc/FAQ/ch-pkg_basics.en.html#s-package">Debian
86 * package</a>.
87 */
88 public static final int DEBIAN_PACKAGE_FILE = 1000;
89
90 /***
91 * A constant for use by the {@link #setFileType(int)} method; indicates that
92 * the file is a binary <a href="http://www.rpm.org/">Red Hat package</a>.
93 */
94 public static final int BINARY_REDHAT_PACKAGE_FILE = 2000;
95
96 /***
97 * A constant for use by the {@link #setFileType(int)} method; indicates that
98 * the file is a <a
99 * href="http://www.gnu.org/manual/gzip-1.2.4/html_mono/gzip.html"><code>zip</code>
100 * archive</a> containing a binary distribution.
101 */
102 public static final int BINARY_ZIP_FILE = 3000;
103
104 /***
105 * A constant for use by the {@link #setFileType(int)} method; indicates that
106 * the file is a <a
107 * href="http://sources.redhat.com/bzip2/"><code>bz2</code></a> archive
108 * containing a binary distribution.
109 */
110 public static final int BINARY_BZIP2_FILE = 3001;
111
112 /***
113 * A constant for use by the {@link #setFileType(int)} method; indicates that
114 * the file is a <a
115 * href="http://www.gnu.org/manual/gzip-1.2.4/html_mono/gzip.html"><code>gzip</code></a>
116 * archive containing a binary distribution.
117 */
118 public static final int BINARY_GZIP_FILE = 3002;
119
120 /***
121 * A constant for use by the {@link #setFileType(int)} method; indicates that
122 * the file is a <a
123 * href="http://www.gnu.org/manual/gzip-1.2.4/html_mono/gzip.html"><code>zip</code>
124 * archive</a> containing a source distribution.
125 */
126 public static final int SOURCE_ZIP_FILE = 5000;
127
128 /***
129 * A constant for use by the {@link #setFileType(int)} method; indicates that
130 * the file is a <a
131 * href="http://sources.redhat.com/bzip2/"><code>bz2</code></a> archive
132 * containing a source distribution.
133 */
134 public static final int SOURCE_BZ2_FILE = 5001;
135
136 /***
137 * A constant for use by the {@link #setFileType(int)} method; indicates that
138 * the file is a <a
139 * href="http://www.gnu.org/manual/gzip-1.2.4/html_mono/gzip.html"><code>gzip</code></a>
140 * archive containing a source distribution.
141 */
142 public static final int SOURCE_GZIP_FILE = 5002;
143
144 /***
145 * A constant for use by the {@link #setFileType(int)} method; indicates that
146 * the file is a <a href="http://www.rpm.org">Red Hat package</a> containing a
147 * source distribution.
148 */
149 public static final int SOURCE_REDHAT_PACKAGE_FILE = 5100;
150
151 /***
152 * A constant for use by the {@link #setFileType(int)} method; indicates that
153 * the file is an otherwise unspecified source distribution or file.
154 *
155 * @see #OTHER_FILE
156 */
157 public static final int OTHER_SOURCE_FILE = 5900;
158
159 /***
160 * A constant for use by the {@link #setFileType(int)} method; indicates that
161 * the file is a <a href="http://www.jpeg.org/jpeg_about.html">JPEG image</a>.
162 */
163 public static final int JPEG_IMAGE_FILE = 8000;
164
165 /***
166 * A constant for use by the {@link #setFileType(int)} method; indicates that
167 * the file is a text file.
168 */
169 public static final int TEXT_FILE = 8001;
170
171 /***
172 * A constant for use by the {@link #setFileType(int)} method; indicates that
173 * the file is an <a href="http://www.w3.org/MarkUp/">HTML</a> page.
174 */
175 public static final int HTML_FILE = 8002;
176
177 /***
178 * A constant for use by the {@link #setFileType(int)} method; indicates that
179 * the file is a <a
180 * href="http://www.adobe.com/products/acrobat/adobepdf.html">PDF</a> file.
181 */
182 public static final int PDF_FILE = 8003;
183
184 /***
185 * A constant for use by the {@link #setFileType(int)} method; indicates that
186 * the file is an otherwise unspecified binary file.
187 *
188 * @see #OTHER_SOURCE_FILE
189 */
190 public static final int OTHER_FILE = 9999;
191
192 /***
193 * A constant for use by the {@link #setFileType(int)} method; indicates that
194 * the file is a <a href="http://www.stuffit.com/">StuffIt</a> archive.
195 */
196 public static final int STUFFIT_FILE = 3003;
197
198 /***
199 * A constant for use by the {@link #setFileType(int)} method; indicates that
200 * the file is a <a
201 * href="http://www.everything2.com/index.pl%3Fnode=nodeball">nodeball</a>.
202 */
203 public static final int NODEBALL_FILE = 3004;
204
205 /***
206 * A constant for use by the {@link #setFileType(int)} method; indicates that
207 * the file is a DOS executable file.
208 */
209 public static final int DOS_EXE_FILE = 2500;
210
211 /***
212 * A constant for use by the {@link #setFileType(int)} method; indicates that
213 * the file is a 16-bit Windows executable file.
214 */
215 public static final int WINDOWS_16_BIT_EXE_FILE = 2501;
216
217 /***
218 * A constant for use by the {@link #setFileType(int)} method; indicates that
219 * the file is a 32-bit Windows executable file.
220 */
221 public static final int WINDOWS_32_BIT_EXE_FILE = 2502;
222
223 /***
224 * A constant for use by the {@link #setFileType(int)} method; indicates that
225 * the file is an OS/2 executable file.
226 */
227 public static final int OS2_EXE_FILE = 2600;
228
229 /***
230 * A constant for use by the {@link #setFileType(int)} method; indicates that
231 * the file is a <a
232 * href="http://developer.apple.com/documentation/UserExperience/Conceptual/AquaHIGuidelines/AHIGDirectories/chapter_15_section_2.html#//apple_ref/doc/uid/20000971/TPXREF103">Macintosh
233 * Disk Image</a> file.
234 */
235 public static final int DMG_FILE = 3005;
236
237 /***
238 * A constant for use by the {@link #setFileType(int)} method; indicates that
239 * the file is a <a
240 * href="http://java.sun.com/j2se/1.4.1/docs/guide/jar/jarGuide.html">jar
241 * file</a>.
242 */
243 public static final int JAR_FILE = 2601;
244
245 /***
246 * A constant for use by the {@link #setFileType(int)} method; indicates that
247 * the file is a <a
248 * href="http://www.gnu.org/manual/diffutils-2.8.1/html_mono/diff.html#Merging%20with%20patch">patch</a>
249 * or <a
250 * href="http://www.gnu.org/manual/diffutils-2.8.1/html_mono/diff.html#Hunks">diff
251 * hunk</a>.
252 */
253 public static final int SOURCE_PATCH_OR_DIFF_FILE = 5901;
254
255 /***
256 * A constant for use by the {@link #setFileType(int)} method; indicates that
257 * the file is a <a
258 * href="http://www.palmos.com/dev/support/docs/palmos/FilesAndDatabases.html#998668">Palm
259 * Resource Database</a>.
260 */
261 public static final int PALM_RESOURCE_DATABASE_FILE = 2700;
262
263 /***
264 * A constant for use by the {@link #setFileType(int)} method; indicates that
265 * the file is an <a href="http://www.filefound.com/answers.html#1">ISO
266 * file</a>.
267 */
268 public static final int ISO_FILE = 3006;
269
270 /***
271 * A constant for use by the {@link #setFileType(int)} method; indicates that
272 * the file is an <a
273 * href="http://www.gnu.org/manual/gzip-1.2.4/html_mono/gzip.html">archive
274 * file generated by the <code>compress</code> program that can be read and
275 * uncompressed with <code>gunzip</code></a>.
276 */
277 public static final int SOURCE_Z_FILE = 5003;
278
279 /***
280 * A constant for use by the {@link #setFileType(int)} method; indicates that
281 * the file is a <a
282 * href="http://www.lazerware.com/formats/macbinary.html">MacBinary</a> file.
283 */
284 public static final int MACBINARY_FILE = 2650;
285
286
287
288
289
290 /***
291 * A constant for use by the {@link #setProcessorType(int)} method; indicates
292 * that the target hardware is a Pentium-class processor.
293 */
294 public static final int I386_PROCESSOR = 1000;
295
296 /***
297 * A constant for use by the {@link #setProcessorType(int)} method; indicates
298 * that the target hardware is a Itanium-class processor.
299 */
300 public static final int IA64_PROCESSOR = 6000;
301
302 /***
303 * A constant for use by the {@link #setProcessorType(int)} method; indicates
304 * that the target hardware is an Alpha processor.
305 */
306 public static final int ALPHA_PROCESSOR = 7000;
307
308 /***
309 * A constant for use by the {@link #setProcessorType(int)} method; indicates
310 * that the target hardware is unimportant.
311 *
312 * @see #PLATFORM_INDEPENDENT_PROCESSOR
313 */
314 public static final int ANY_PROCESSOR = 8000;
315
316 /***
317 * A constant for use by the {@link #setProcessorType(int)} method; indicates
318 * that the target hardware is a PowerPC processor.
319 */
320 public static final int PPC_PROCESSOR = 2000;
321
322 /***
323 * A constant for use by the {@link #setProcessorType(int)} method; indicates
324 * that the target hardware is a MIPS processor.
325 */
326 public static final int MIPS_PROCESSOR = 3000;
327
328 /***
329 * A constant for use by the {@link #setProcessorType(int)} method; indicates
330 * that the target hardware is a SPARC processor.
331 */
332 public static final int SPARC_PROCESSOR = 4000;
333
334 /***
335 * A constant for use by the {@link #setProcessorType(int)} method; indicates
336 * that the target hardware is an UltraSPARC processor.
337 */
338 public static final int ULTRASPARC_PROCESSOR = 5000;
339
340 /***
341 * A constant for use by the {@link #setProcessorType(int)} method; indicates
342 * that the target hardware is unimportant.
343 *
344 * @see #ANY_PROCESSOR
345 */
346 public static final int PLATFORM_INDEPENDENT_PROCESSOR = 8500;
347
348 /***
349 * A {@link Map} that indexes file type constants by filename suffix. For
350 * example, a "<code>.deb</code>" suffix is mapped to the {@link
351 * #DEBIAN_PACKAGE_FILE} constant.
352 */
353 protected static final Map SUFFIX_TO_FILE_TYPE_MAP;
354
355 /***
356 * Static initializer; initializes the {@link #SUFFIX_TO_FILE_TYPE_MAP} field.
357 */
358 static {
359
360 final Map map = new HashMap(31, 1F);
361 map.put(".deb", new Integer(DEBIAN_PACKAGE_FILE));
362 map.put(".rpm", new Integer(BINARY_REDHAT_PACKAGE_FILE));
363 map.put(".zip", new Integer(BINARY_ZIP_FILE));
364 map.put(".bz2", new Integer(BINARY_BZIP2_FILE));
365 map.put(".gz", new Integer(BINARY_GZIP_FILE));
366 map.put(".tgz", new Integer(BINARY_GZIP_FILE));
367 map.put(".jpg", new Integer(JPEG_IMAGE_FILE));
368 map.put(".jpeg", new Integer(JPEG_IMAGE_FILE));
369 map.put(".txt", new Integer(TEXT_FILE));
370 map.put(".text", new Integer(TEXT_FILE));
371 map.put(".htm", new Integer(HTML_FILE));
372 map.put(".html", new Integer(HTML_FILE));
373 map.put(".pdf", new Integer(PDF_FILE));
374 map.put(".sit", new Integer(STUFFIT_FILE));
375 map.put(".nbz", new Integer(NODEBALL_FILE));
376 map.put(".nbz", new Integer(NODEBALL_FILE));
377 map.put(".exe", new Integer(WINDOWS_32_BIT_EXE_FILE));
378 map.put(".dmg", new Integer(DMG_FILE));
379 map.put(".jar", new Integer(JAR_FILE));
380 map.put(".diff", new Integer(SOURCE_PATCH_OR_DIFF_FILE));
381 map.put(".patch", new Integer(SOURCE_PATCH_OR_DIFF_FILE));
382 map.put(".prc", new Integer(PALM_RESOURCE_DATABASE_FILE));
383 map.put(".iso", new Integer(ISO_FILE));
384 map.put(".z", new Integer(SOURCE_Z_FILE));
385 map.put(".bin", new Integer(MACBINARY_FILE));
386 SUFFIX_TO_FILE_TYPE_MAP =
387 Collections.synchronizedMap(Collections.unmodifiableMap(map));
388 }
389
390 /***
391 * The type of processor this {@link FileSpecification}'s enclosing {@link
392 * FileRelease} is destined for. Processor type constants are defined
393 * elsewhere in this class; see, for example, the {@link #ANY_PROCESSOR}
394 * field.
395 *
396 * @see #ANY_PROCESSOR
397 */
398 private int processorType;
399
400 /***
401 * The {@link Date} on which this {@link FileSpecification} is to be
402 * considered released.
403 *
404 * <p>I am not sure why <a href="http://sourceforge.net/">SourceForge</a>
405 * makes this an attribute of a file specification as well as of its enclosing
406 * file release, since a file specification cannot be released independent of
407 * a file release.</p>
408 *
409 * <p>This field may be <code>null</code>, in which case the current {@link
410 * Date} should be used instead.</p>
411 */
412 private Date releaseDate;
413
414 /***
415 * The {@link File} that this {@link FileSpecification} adds additional
416 * attributes to. This field may be <code>null</code>.
417 */
418 private File file;
419
420 /***
421 * The type of file this {@link FileSpecification} represents. File type
422 * constants are defined elsewhere in this class; see, for example, the {@link
423 * #BINARY_BZIP2_FILE} field.
424 *
425 * @see #BINARY_BZIP2_FILE
426 */
427 private int fileType;
428
429 /***
430 * Creates a new {@link FileSpecification}. The {@linkplain #getFileType()
431 * file type} is initialized to {@link #OTHER_FILE}, the {@linkplain
432 * #getProcessorType() processor type} is initialized to {@link
433 * #ANY_PROCESSOR}, and the {@linkplain #getReleaseDate() release date} is
434 * initialized to a new {@link Date}.
435 *
436 * <p>The resulting {@link FileSpecification} object will be in an illegal
437 * state until its {@link #setFile(File)} method is called with a valid
438 * argument.</p>
439 *
440 * @see #setFile(File)
441 */
442 public FileSpecification() {
443 super();
444 this.setFileType(OTHER_FILE);
445 this.setProcessorType(ANY_PROCESSOR);
446 this.setReleaseDate(new Date());
447 }
448
449 /***
450 * Creates a new {@link FileSpecification}. This constructor calls the {@link
451 * #FileSpecification()} constructor and then calls the {@link #setFile(File)}
452 * method with the supplied {@link File}.
453 *
454 * @param file
455 * the {@link File} this {@link FileSpecification} represents;
456 * must not be <code>null</code> and must meet the other
457 * <a href="#fileReqs">SourceForge filename requirements</a>
458 * @exception IllegalArgumentException
459 * if the supplied {@link File} does not meet the <a
460 * href="#fileReqs">SourceForge filename requirements</a>
461 */
462 public FileSpecification(final File file)
463 throws IllegalArgumentException {
464 this();
465 this.setFile(file);
466 }
467
468 /***
469 * Returns the type of processor this {@link FileSpecification} targets. The
470 * return value will be one of the processor type constants defined elsewhere
471 * in this class. See, for example, the {@link #ANY_PROCESSOR} field.
472 *
473 * @return the type of processor this {@link FileSpecification} targets
474 * @see #ANY_PROCESSOR
475 */
476 public int getProcessorType() {
477 return this.processorType;
478 }
479
480 /***
481 * Sets the type of the processor this {@link FileSpecification}'s enclosing
482 * {@link FileRelease} is destined for. The supplied value must be one of
483 * {@link #I386_PROCESSOR}, {@link #IA64_PROCESSOR}, {@link #ALPHA_PROCESSOR},
484 * {@link #ANY_PROCESSOR}, {@link #PPC_PROCESSOR}, {@link #MIPS_PROCESSOR},
485 * {@link #SPARC_PROCESSOR}, {@link #ULTRASPARC_PROCESSOR} or {@link
486 * #PLATFORM_INDEPENDENT_PROCESSOR}.
487 *
488 * @param processorType
489 * the type of processor this {@link FileSpecification}'s
490 * enclosing {@link FileRelease} is destined for; must be one of
491 * {@link #I386_PROCESSOR}, {@link #IA64_PROCESSOR}, {@link
492 * #ALPHA_PROCESSOR}, {@link #ANY_PROCESSOR}, {@link
493 * #PPC_PROCESSOR}, {@link #MIPS_PROCESSOR}, {@link
494 * #SPARC_PROCESSOR}, {@link #ULTRASPARC_PROCESSOR} or {@link
495 * #PLATFORM_INDEPENDENT_PROCESSOR}
496 */
497 public void setProcessorType(int processorType) {
498 switch (processorType) {
499 case ALPHA_PROCESSOR:
500 case ANY_PROCESSOR:
501 case I386_PROCESSOR:
502 case IA64_PROCESSOR:
503 case MIPS_PROCESSOR:
504 case PLATFORM_INDEPENDENT_PROCESSOR:
505 case PPC_PROCESSOR:
506 case SPARC_PROCESSOR:
507 case ULTRASPARC_PROCESSOR:
508 break;
509 default:
510 processorType = ANY_PROCESSOR;
511 }
512 this.processorType = processorType;
513 }
514
515 /***
516 * Converts the supplied {@link String} into a suitable processor type
517 * constant. The supplied {@link String} must be one of "{@link
518 * #I386_PROCESSOR}", "{@link #IA64_PROCESSOR}", "{@link
519 * #ALPHA_PROCESSOR}", "{@link #ANY_PROCESSOR}", "{@link
520 * #PPC_PROCESSOR}", "{@link #MIPS_PROCESSOR}", "{@link
521 * #SPARC_PROCESSOR}", "{@link #ULTRASPARC_PROCESSOR}" or
522 * "{@link #PLATFORM_INDEPENDENT_PROCESSOR}".
523 *
524 * @param type
525 * a {@link String} representation of the {@linkplain
526 * #setProcessorType(int) processor type to set}; must be one of
527 * "{@link #I386_PROCESSOR}", "{@link
528 * #IA64_PROCESSOR}", "{@link #ALPHA_PROCESSOR}",
529 * "{@link #ANY_PROCESSOR}", "{@link
530 * #PPC_PROCESSOR}", "{@link #MIPS_PROCESSOR}",
531 * "{@link #SPARC_PROCESSOR}", "{@link
532 * #ULTRASPARC_PROCESSOR}" or "{@link
533 * #PLATFORM_INDEPENDENT_PROCESSOR}"
534 * @exception IllegalArgumentException
535 * if the supplied {@link String} is not one of "{@link
536 * #I386_PROCESSOR}", "{@link #IA64_PROCESSOR}",
537 * "{@link #ALPHA_PROCESSOR}", "{@link
538 * #ANY_PROCESSOR}", "{@link #PPC_PROCESSOR}",
539 * "{@link #MIPS_PROCESSOR}", "{@link
540 * #SPARC_PROCESSOR}", "{@link
541 * #ULTRASPARC_PROCESSOR}" or "{@link
542 * #PLATFORM_INDEPENDENT_PROCESSOR}"
543 */
544 public void setProcessorTypeString(String type)
545 throws IllegalArgumentException {
546 this.setTypeString(type, false);
547 }
548
549 /***
550 * Returns the {@link File} that this {@link FileSpecification} represents.
551 * This method never returns <code>null</code> and will throw an {@link
552 * IllegalStateException} if the {@link #setFile(File)} method has not yet
553 * been called with a non-<code>null</code> argument.
554 *
555 * @return the {@link File} that this {@link FileSpecification}
556 * represents; never <code>null</code>
557 * @exception IllegalStateException
558 * if the {@link File} associated with this {@link
559 * FileSpecification} is <code>null</code> for any reason
560 */
561 public File getFile()
562 throws IllegalStateException {
563 if (this.file == null) {
564 throw new IllegalStateException("this.file == null");
565 }
566 return this.file;
567 }
568
569 /***
570 * Sets the {@link File} that this {@link FileSpecification} will represent.
571 * The supplied {@link File} must conform to the <a
572 * href="#fileReqs">SourceForge-imposed filename restrictions</a>.
573 *
574 * <p>If possible, the {@linkplain #setFileType(int) file type} will be set
575 * appropriately from the supplied {@link File}'s suffix.</p>
576 *
577 * @param file
578 * the {@link File} that this {@link FileSpecification} will
579 * represent; must be non-<code>null</code>, must {@linkplain
580 * File#canRead() exist and be readable}, must be between 20 and
581 * 256,000 bytes in {@linkplain File#length() length}, and must
582 * not contain a space, tilde or parenthesis in its {@linkplain
583 * File#getName() name}
584 * @exception IllegalArgumentException
585 * if the supplied {@link File} does not satisfy the <a
586 * href="#fileReqs">requirements above</a>
587 */
588 public void setFile(final File file)
589 throws IllegalArgumentException {
590 validate(file);
591 assert file != null;
592 final int fileType = computeFileType(file);
593 if (isValidFileType(fileType)) {
594 this.setFileType(fileType);
595 } else {
596 this.setFileType(OTHER_FILE);
597 }
598 this.file = file;
599 }
600
601 /***
602 * Ensures that the supplied {@link File} meets the <a
603 * href="#fileReqs">SourceForge-imposed filename requirements</a>.
604 *
605 * @param file
606 * the {@link File} to be validated; may be <code>null</code>,
607 * although this will cause an {@link IllegalArgumentException}
608 * to be thrown
609 * @exception IllegalArgumentException
610 * if the supplied {@link File} does not meet all of the <a
611 * href="#fileReqs">SourceForge-imposed filename
612 * requirements</a>
613 */
614 public static final void validate(final File file)
615 throws IllegalArgumentException {
616 if (file == null) {
617 throw new IllegalArgumentException("file is null");
618 }
619 if (!file.canRead()) {
620 throw new IllegalArgumentException("file must exist and must be " +
621 "readable");
622 }
623 final String name = file.getName();
624 assert name != null;
625 if (name.length() <= 0) {
626 throw new IllegalArgumentException("file name must be at least one " +
627 "character long");
628 }
629 if (name.indexOf(" ") >= 0 ||
630 name.indexOf(")") >= 0 ||
631 name.indexOf("(") >= 0 ||
632 name.indexOf("~") >= 0) {
633 throw new IllegalArgumentException("file name cannot contain a space, " +
634 "tilde or parenthesis");
635 }
636 }
637
638 /***
639 * Returns a {@link File} type for the supplied {@link File} based on its
640 * suffix. If a suitable {@link File} type cannot be determined, then {@link
641 * #OTHER_FILE} is returned.
642 *
643 * @param file
644 * the {@link File} for which a {@link File} type should be
645 * returned; must not be <code>null</code> and must meet the <a
646 * href="#fileReqs">SourceForge-imposed filename
647 * requirements</a>
648 * @return a suitable {@link File} type for the supplied {@link File}
649 * @exception IllegalArgumentException
650 * if the supplied {@link File} does not meet all of the <a
651 * href="#fileReqs">SourceForge-imposed filename
652 * requirements</a>
653 */
654 public static int computeFileType(final File file)
655 throws IllegalArgumentException {
656 validate(file);
657 assert file != null;
658 final String name = file.getName();
659 assert name != null;
660 assert name.length() > 0;
661 final int lastPeriodIndex = name.lastIndexOf('.');
662 if (lastPeriodIndex >= 0 && lastPeriodIndex != name.length() - 1) {
663 String suffix = name.substring(lastPeriodIndex);
664 assert suffix != null;
665 suffix = suffix.toLowerCase();
666 assert suffix != null;
667 final Integer type = (Integer)SUFFIX_TO_FILE_TYPE_MAP.get(suffix);
668 if (type == null) {
669 return OTHER_FILE;
670 }
671 final int fileType = type.intValue();
672 if (isValidFileType(fileType)) {
673 return fileType;
674 }
675 }
676 return OTHER_FILE;
677 }
678
679 /***
680 * Returns <code>true</code> if and only if the supplied <code>int</code> is
681 * one of the {@link File} type constants defined elsewhere in this class.
682 *
683 * @param type
684 * the type to be tested
685 * @return <code>true</code> if and only if the supplied <code>int</code>
686 * is one of the {@link File} type constants defined elsewhere
687 * in this class
688 */
689 protected static boolean isValidFileType(final int type) {
690 switch (type) {
691 case BINARY_BZIP2_FILE:
692 case BINARY_GZIP_FILE:
693 case BINARY_REDHAT_PACKAGE_FILE:
694 case BINARY_ZIP_FILE:
695 case DEBIAN_PACKAGE_FILE:
696 case DMG_FILE:
697 case DOS_EXE_FILE:
698 case HTML_FILE:
699 case ISO_FILE:
700 case JAR_FILE:
701 case JPEG_IMAGE_FILE:
702 case MACBINARY_FILE:
703 case NODEBALL_FILE:
704 case OS2_EXE_FILE:
705 case OTHER_FILE:
706 case OTHER_SOURCE_FILE:
707 case PALM_RESOURCE_DATABASE_FILE:
708 case PDF_FILE:
709 case SOURCE_BZ2_FILE:
710 case SOURCE_GZIP_FILE:
711 case SOURCE_PATCH_OR_DIFF_FILE:
712 case SOURCE_REDHAT_PACKAGE_FILE:
713 case SOURCE_ZIP_FILE:
714 case SOURCE_Z_FILE:
715 case STUFFIT_FILE:
716 case TEXT_FILE:
717 case WINDOWS_16_BIT_EXE_FILE:
718 case WINDOWS_32_BIT_EXE_FILE:
719 return true;
720 default:
721 return false;
722 }
723 }
724
725 /***
726 * Returns the type of the {@link File} that this {@link FileSpecification}
727 * represents.
728 *
729 * @return one of the {@link File} type constants defined elsewhere in
730 * this class; e.g. {@link #OTHER_FILE}
731 * @see #OTHER_FILE
732 */
733 public int getFileType() {
734 return this.fileType;
735 }
736
737 /***
738 * Sets the type of {@link File} that this {@link FileSpecification}
739 * represents. The supplied {@link File} type must be one of the {@link File}
740 * types declared elsewhere in this class. For example, see the {@link
741 * #OTHER_FILE} field.
742 *
743 * @param type
744 * the new {@link File} type; must be one of the {@link File}
745 * types declared elsewhere in this class; e.g. {@link
746 * #OTHER_FILE}
747 * @see #OTHER_FILE
748 */
749 public void setFileType(final int type) {
750 if (isValidFileType(type)) {
751 this.fileType = type;
752 } else {
753 this.fileType = OTHER_FILE;
754 }
755 }
756
757 /***
758 * Sets the type of {@link File} that this {@link FileSpecification}
759 * represents. The supplied {@link String} must be equal to the name of a
760 * {@link File} type constant defined elsewhere in this class. See, for
761 * example, the {@link #OTHER_FILE} constant.
762 *
763 * @param type
764 * the new {@link File} type; must not be <code>null</code> and
765 * must be equal to the name of a {@link File} type constant
766 * defined elsewhere in this class
767 * @exception IllegalArgumentException
768 * if the supplied {@link File} type {@link String} was not
769 * valid
770 */
771 public void setFileTypeString(final String type)
772 throws IllegalArgumentException {
773 this.setTypeString(type, true);
774 }
775
776 /***
777 * A convenience method called by the {@link #setFileTypeString(String)} and
778 * {@link #setProcessorTypeString(String)} methods that translates its {@link
779 * String} argument into the proper constant value. For example, an argument
780 * of "<code>OTHER_FILE</code>" would be translated into the value
781 * of the {@link #OTHER_FILE} field, and the {@link #setFileType(int)} method
782 * would be called with that value as an argument.
783 *
784 * @param type
785 * the {@link String} value that hopefully corresponds to a
786 * valid "type" constant; must not be <code>null</code>
787 * @param fileType
788 * if <code>true</code>, then it is understood that the supplied
789 * {@link String} will be treated as a {@link File} type;
790 * otherwise it will be treated as a processor type
791 * @exception IllegalArgumentException
792 * if <code>type</code> is <code>null</code> or is an unknown
793 * type
794 */
795 private void setTypeString(final String type, final boolean fileType)
796 throws IllegalArgumentException {
797
798 final String kind;
799 final String fieldNameSuffix;
800 if (fileType) {
801 kind = "File";
802 fieldNameSuffix = "_FILE";
803 } else {
804 kind = "Processor";
805 fieldNameSuffix = "_PROCESSOR";
806 }
807
808 String workingType = type;
809 if (workingType == null) {
810 throw new IllegalArgumentException(kind + " type cannot be null");
811 }
812 workingType = workingType.trim();
813 if (workingType == null) {
814 throw new InternalError("String.trim() returned null");
815 }
816 workingType = workingType.replace(' ', '_');
817 if (workingType == null) {
818 throw new InternalError("String.replace() returned null");
819 }
820 if (!workingType.endsWith(fieldNameSuffix)) {
821 workingType = workingType + fieldNameSuffix;
822 }
823 workingType = workingType.toUpperCase();
824
825
826
827 final Class myClass = this.getClass();
828 if (myClass == null) {
829 throw new InternalError("Object.getClass() returned null");
830 }
831 Field f = null;
832 try {
833 f = myClass.getField(workingType);
834 } catch (final NoSuchFieldException kaboom) {
835 throw new IllegalArgumentException("Unknown " + kind + " type: " + type);
836 }
837 if (f == null) {
838 throw new IllegalArgumentException("Unknown " + kind + " type: " + type);
839 }
840 final int modifiers = f.getModifiers();
841 if (!Modifier.isStatic(modifiers) ||
842 !Modifier.isFinal(modifiers) ||
843 !Integer.TYPE.equals(f.getType())) {
844 throw new IllegalArgumentException("Unknown " + kind + " type: " + type);
845 }
846 try {
847 if (fileType) {
848 this.setFileType(f.getInt(this));
849 } else {
850 this.setProcessorType(f.getInt(this));
851 }
852 } catch (final IllegalAccessException wontHappen) {
853 throw new IllegalArgumentException("Unknown " + kind + " type: " + type);
854 }
855 }
856
857 /***
858 * Returns the {@link Date} on which the {@linkplain #getFile()
859 * <code>File</code> associated with this <code>FileSpecification</code>} is
860 * to be released. This method may return <code>null</code>, in which case
861 * the current {@link Date} should be used instead.
862 *
863 * @return the {@link Date} on which the {@linkplain #getFile()
864 * <code>File</code> associated with this
865 * <code>FileSpecification</code>} is to be released, or
866 * <code>null</code> if the current {@link Date} should be used
867 * instead
868 */
869 public Date getReleaseDate() {
870 return this.releaseDate;
871 }
872
873 /***
874 * Sets the {@link Date} on which the {@linkplain #getFile()
875 * <code>File</code> associated with this <code>FileSpecification</code>} is
876 * to be released.
877 *
878 * @param date
879 * the new {@link Date} on which the {@linkplain #getFile()
880 * <code>File</code> associated with this
881 * <code>FileSpecification</code>} is to be released; may be
882 * <code>null</code>, in which case the current {@link Date}
883 * will be used instead
884 */
885 public void setReleaseDate(final Date date) {
886 if (date == null) {
887 this.releaseDate = new Date();
888 } else {
889 this.releaseDate = date;
890 }
891 }
892
893 /***
894 * Returns a hashcode for this {@link FileSpecification} based on its
895 * {@linkplain #getFile() associated <code>File</code>}.
896 *
897 * @return a hashcode for this {@link FileSpecification}
898 */
899 public int hashCode() {
900 File file = null;
901 try {
902 file = this.getFile();
903 } catch (final IllegalStateException ignore) {
904 file = null;
905 }
906 if (file == null) {
907 return 0;
908 }
909 return file.hashCode();
910 }
911
912 /***
913 * Tests the supplied {@link Object} to see if it is equal to this {@link
914 * FileSpecification}. An {@link Object} is equal to this {@link
915 * FileSpecification} if it is an instance of the {@link FileSpecification}
916 * class and its {@linkplain #getFile() associated <code>File</code>} is equal
917 * to this {@link FileSpecification}'s {@linkplain #getFile() associated
918 * <code>File</code>}.
919 *
920 * @param anObject
921 * the {@link Object} to test; may be <code>null</code>
922 * @return <code>true</code> if and only if the supplied {@link Object} is
923 * equal to this {@link FileSpecification}
924 */
925 public boolean equals(final Object anObject) {
926 if (anObject == this) {
927 return true;
928 } else if (anObject instanceof FileSpecification) {
929 final FileSpecification other = (FileSpecification)anObject;
930 File file = null;
931 try {
932 file = this.getFile();
933 } catch (final IllegalStateException ignore) {
934 file = null;
935 }
936 File otherFile = null;
937 try {
938 otherFile = this.getFile();
939 } catch (final IllegalStateException ignore) {
940 otherFile = null;
941 }
942 if (file == null) {
943 return otherFile == null;
944 }
945 return file.equals(otherFile);
946 } else {
947 return false;
948 }
949 }
950
951 /***
952 * Returns a {@link String} representation of this {@link FileSpecification}.
953 * This method never returns <code>null</code>.
954 *
955 * @return a {@link String} representation of this {@link
956 * FileSpecification}; never <code>null</code>
957 */
958 public String toString() {
959 File file = null;
960 try {
961 file = this.getFile();
962 } catch (final IllegalStateException ignore) {
963 file = null;
964 }
965 if (file == null) {
966 return "Uninitialized FileSpecification";
967 }
968 return file.getName();
969 }
970
971 }