@Override
 public void reload() throws IOException {
   ZipFile zipFile;
   if (this.file == null) {
     zipFile = ((JarURLConnection) this.url.openConnection()).getJarFile();
   } else {
     zipFile = new ZipFile(this.file);
   }
   // collect the relevant entries
   Map<ResourceKind, Map<String, ZipEntry>> zipEntryMap =
       new EnumMap<ResourceKind, Map<String, ZipEntry>>(ResourceKind.class);
   for (ResourceKind kind : ResourceKind.values()) {
     zipEntryMap.put(kind, new HashMap<String, ZipEntry>());
   }
   ZipEntry properties = null;
   for (Enumeration<? extends ZipEntry> entries = zipFile.entries(); entries.hasMoreElements(); ) {
     ZipEntry entry = entries.nextElement();
     String entryName = entry.getName();
     int entryPrefixLength = this.entryName.length() + 1;
     if (entryName.startsWith(this.entryName) && entryName.length() > entryPrefixLength) {
       // strip off prefix + 1 to take case of file separator
       String restName = entryName.substring(entryPrefixLength);
       // find out the resource kind by testing the extension
       ResourceKind kind = null;
       for (ResourceKind tryKind : ResourceKind.values()) {
         if (restName.endsWith(tryKind.getFileType().getExtension())) {
           kind = tryKind;
           break;
         }
       }
       if (kind == PROPERTIES) {
         String propertiesName = PROPERTIES.getFileType().stripExtension(restName);
         if (propertiesName.equals(Groove.PROPERTY_NAME)) {
           // preferably take the one with the default name
           properties = entry;
         } else if (properties == null && propertiesName.equals(this.grammarName)) {
           // otherwise, take the one with the grammar name
           properties = entry;
         }
       } else if (kind == null) {
         // must be a layout file
         if (LAYOUT.hasExtension(restName)) {
           String objectName = LAYOUT.stripExtension(restName);
           this.layoutEntryMap.put(objectName, entry);
         }
       } else {
         Object oldEntry = zipEntryMap.get(kind).put(restName, entry);
         assert oldEntry == null
             : String.format("Duplicate %s name '%s'", kind.getName(), restName);
       }
     }
   }
   // now process the entries
   // first load the properties, as they are necessary for loading types
   loadProperties(zipFile, properties);
   for (ResourceKind kind : ResourceKind.values()) {
     if (kind.isTextBased()) {
       loadTexts(kind, zipFile, zipEntryMap.get(kind));
     } else if (kind.isGraphBased()) {
       loadGraphs(kind, zipFile, zipEntryMap.get(kind));
     }
   }
   zipFile.close();
   notify(EnumSet.allOf(ResourceKind.class));
   this.initialised = true;
 }