View Javadoc

1   /* -*- mode: JDE; c-basic-offset: 2; indent-tabs-mode: nil -*-
2    *
3    * $Id: FileSpecification.java,v 1.10 2003/07/13 02:47:02 ljnelson Exp $
4    *
5    * Copyright (c) 2003 Laird Jarrett Nelson.
6    *
7    * Permission is hereby granted, free of charge, to any person obtaining a copy
8    * of this software and associated documentation files (the "Software"), to deal
9    * in the Software without restriction, including without limitation the rights
10   * to use, copy, modify, merge, publish, distribute, sublicense and/or sell 
11   * copies of the Software, and to permit persons to whom the Software is 
12   * furnished to do so, subject to the following conditions:
13   * 
14   * The above copyright notice and this permission notice shall be included in 
15   * all copies or substantial portions of the Software.
16   * 
17   * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
19   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
20   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
21   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23   * SOFTWARE.
24   *
25   * The original copy of this license is available at
26   * http://www.opensource.org/license/mit-license.html.
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; // for Javadoc only
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    * Processor types
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     // TODO: dynamicize
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 &quot;{@link
518    * #I386_PROCESSOR}&quot;, &quot;{@link #IA64_PROCESSOR}&quot;, &quot;{@link
519    * #ALPHA_PROCESSOR}&quot;, &quot;{@link #ANY_PROCESSOR}&quot;, &quot;{@link
520    * #PPC_PROCESSOR}&quot;, &quot;{@link #MIPS_PROCESSOR}&quot;, &quot;{@link
521    * #SPARC_PROCESSOR}&quot;, &quot;{@link #ULTRASPARC_PROCESSOR}&quot; or
522    * &quot;{@link #PLATFORM_INDEPENDENT_PROCESSOR}&quot;.
523    *
524    * @param      type
525    *               a {@link String} representation of the {@linkplain
526    *               #setProcessorType(int) processor type to set}; must be one of
527    *               &quot;{@link #I386_PROCESSOR}&quot;, &quot;{@link
528    *               #IA64_PROCESSOR}&quot;, &quot;{@link #ALPHA_PROCESSOR}&quot;,
529    *               &quot;{@link #ANY_PROCESSOR}&quot;, &quot;{@link
530    *               #PPC_PROCESSOR}&quot;, &quot;{@link #MIPS_PROCESSOR}&quot;,
531    *               &quot;{@link #SPARC_PROCESSOR}&quot;, &quot;{@link
532    *               #ULTRASPARC_PROCESSOR}&quot; or &quot;{@link
533    *               #PLATFORM_INDEPENDENT_PROCESSOR}&quot;
534    * @exception  IllegalArgumentException
535    *               if the supplied {@link String} is not one of &quot;{@link
536    *               #I386_PROCESSOR}&quot;, &quot;{@link #IA64_PROCESSOR}&quot;,
537    *               &quot;{@link #ALPHA_PROCESSOR}&quot;, &quot;{@link
538    *               #ANY_PROCESSOR}&quot;, &quot;{@link #PPC_PROCESSOR}&quot;,
539    *               &quot;{@link #MIPS_PROCESSOR}&quot;, &quot;{@link
540    *               #SPARC_PROCESSOR}&quot;, &quot;{@link
541    *               #ULTRASPARC_PROCESSOR}&quot; or &quot;{@link
542    *               #PLATFORM_INDEPENDENT_PROCESSOR}&quot; 
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 &quot;<code>OTHER_FILE</code>&quot; 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     // Since there are so many types, let's just use reflection to see if
826     // there is a constant that is named the same thing.
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 }