Additions to FormNode(?)

I'm working on a FitsForm.save() method and in the process I wrote a
cross-checking program which contains the following code:

        public Data read(String name)
                throws BadFormException, IOException, VisADException
        {
          Form form;

          form = guessFormFromName(name);
          if (form != null) {
            try {
              Data d = form.open(name);
              return d;
            } catch (Exception e) {
              form = null;
            }
          }

          try {
            Data d = new Plain().open(name);
            return d;
          } catch (Exception e) {
          }

          try {
            Data d = new FitsForm().open(name);
            return d;
          } catch (Exception e) {
          }

          throw new VisADException("Couldn't read \"" + name + "\"");
        }

        public Form guessFormFromName(String name)
        {
          if (name.endsWith(".Z")) {
            name = name.substring(0, name.length() - 2);
          } else if (name.endsWith(".gz")) {
            name = name.substring(0, name.length() - 3);
          }

          if (name.endsWith(".nc")) {
            return new Plain();
          }

          if (name.endsWith(".fits")) {
            return new FitsForm();
          }

          return null;
        }

(Yes, I know there are better ways to write this, but it's a bloody debugging
script, not the ceiling of the Sistine Chapel)

It'd sure be nice to push the logic from guessFormFromName() into the
individual Form classes and augment that with code which recognizes
the file type based on the first block.  For example, FITS files always
start with "SIMPLE  = ", GIF files always start with "GIF87a" or "GIF89a",
etc.

Could we maybe add these methods to FormNode (I *hate* the method name but
couldn't come up with anything better):

        public abstract boolean isThisType(String name)
                Returns true if this file name looks like it might
                be associated with a file of this type.

                Note that the file associated with this name may still
                be of this type, even though this function returns false.

        public abstract boolean isThisType(byte[] block)
                Returns true if this 512-byte block appears to be the
                first block of a file of this type.

I think something like this would greatly speed up opening arbitrary files.

Given the above changes, my test-jacket (and the Repository class) could be
implemented like this:

        constructor
        {
          // build a list of all forms
        }

        public Data save(String name)
        {
          // make a copy of the form list

          // build sublist of all forms which return true for isThisType(name)

          if (sublist.length == 1) {

            // try to read this file
            try {
              Data d = sublist[0].open(name);
            } catch (Exception e) {
            }

            // didn't work; throw out this form
            copiedList = remove(copiedList, sublist);
          } else {
            // multiple matches for this filename; check magic for each one
            Data d = checkMagic(sublist, name);
            if (d != null) {
              return d;
            }
          }

          // try to open files whose magic number matches the file type
          Data d = checkMagic(copiedFile);
          if (d != null) {
            return d;
          }

          // for each entry remaining in the copiedList, try to read it in
          //    and return the resulting Data object if it succeeds
        }

        private Data checkMagic(Form[] list, String name)
        {
          byte[] block = first 512 bytes in "name"
          for each entry in the list {
            if (entry.isThisType(block)) {
              try {
                Data d = entry.open(name);
                return d;
              } catch (Exception e) {
                // add entry to the list of forms that were tried
                triedForms.add(entry);
              }
            }
          }

          // remove the failed forms from the copied list
          copiedList = remove(copiedList, triedForms);
        }