/**
  * Loads the named objects from a zip file, using the prepared map from names to zip entries.
  * Returns a map from names to {@link AspectGraph}s.
  *
  * @param file the file to read from
  * @param graphs the mapping from object names to zip entries
  */
 private Map<String, AspectGraph> loadObjects(
     ResourceKind kind, ZipFile file, Map<String, ZipEntry> graphs) throws IOException {
   FileType filter = kind.getFileType();
   Map<String, AspectGraph> result = new HashMap<String, AspectGraph>();
   for (Map.Entry<String, ZipEntry> graphEntry : graphs.entrySet()) {
     String graphName = filter.stripExtension(graphEntry.getKey());
     InputStream in = file.getInputStream(graphEntry.getValue());
     try {
       AttrGraph xmlGraph = GxlIO.instance().loadGraph(in);
       /*
        * For backward compatibility, we set the role and name of the
        * graph.
        */
       xmlGraph.setRole(kind.getGraphRole());
       xmlGraph.setName(createQualName(graphName).toString());
       addLayout(file, graphEntry.getKey(), xmlGraph);
       AspectGraph graph = xmlGraph.toAspectGraph();
       /* Store the graph */
       result.put(graphName, graph);
     } catch (FormatException exc) {
       throw new IOException(
           String.format("Format error while loading '%s':\n%s", graphName, exc.getMessage()),
           exc);
     } catch (IOException exc) {
       throw new IOException(
           String.format("Error while loading '%s':\n%s", graphName, exc.getMessage()), exc);
     }
   }
   return result;
 }
 /**
  * Loads the named control programs from a zip file, using the prepared map from program names to
  * zip entries.
  */
 private void loadTexts(ResourceKind kind, ZipFile file, Map<String, ZipEntry> texts)
     throws IOException {
   getTextMap(kind).clear();
   for (Map.Entry<String, ZipEntry> textEntry : texts.entrySet()) {
     String controlName = kind.getFileType().stripExtension(textEntry.getKey());
     InputStream in = file.getInputStream(textEntry.getValue());
     String program = groove.io.Util.readInputStreamToString(in);
     getTextMap(kind).put(controlName, program);
   }
 }
 @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;
 }