public String protocolsMissingUploaders(
     final UploaderService uploaderService, final Progress progress) {
   final Map<String, Set<String>> map = new LinkedHashMap<String, Set<String>>();
   for (final Map.Entry<String, UpdateSite> entry : updateSites.entrySet()) {
     final UpdateSite site = entry.getValue();
     if (!site.isUploadable()) continue;
     final String protocol = site.getUploadProtocol();
     try {
       uploaderService.installUploader(protocol, this, progress);
     } catch (IllegalArgumentException e) {
       Set<String> set = map.get(protocol);
       if (set == null) {
         set = new LinkedHashSet<String>();
         map.put(protocol, set);
       }
       set.add(entry.getKey());
     }
   }
   if (map.size() == 0) return null;
   final StringBuilder builder = new StringBuilder();
   builder.append(
       prefixUpdate("").isDirectory()
           ? "Uploads via these protocols require a restart:\n"
           : "Missing uploaders:\n");
   for (final Map.Entry<String, Set<String>> entry : map.entrySet()) {
     final String list = Arrays.toString(entry.getValue().toArray());
     builder.append("'").append(entry.getKey()).append("': ").append(list).append("\n");
   }
   return builder.toString();
 }
 public Set<GroupAction> getValidActions() {
   final Set<GroupAction> actions = new LinkedHashSet<GroupAction>();
   actions.add(new KeepAsIs());
   boolean hasChanges = hasChanges(), hasUploadOrRemove = hasUploadOrRemove();
   if (!hasUploadOrRemove) {
     actions.add(new InstallOrUpdate());
   }
   if (hasUploadOrRemove || !hasChanges) {
     final Collection<String> siteNames = getSiteNamesToUpload();
     final Map<String, UpdateSite> updateSites;
     if (siteNames.size() == 0) updateSites = this.updateSites;
     else {
       updateSites = new LinkedHashMap<String, UpdateSite>();
       for (final String name : siteNames) {
         updateSites.put(name, getUpdateSite(name, true));
       }
     }
     for (final UpdateSite updateSite : getUpdateSites(false)) {
       if (updateSite.isUploadable()) {
         final String name = updateSite.getName();
         actions.add(new Upload(name));
         actions.add(new Remove(name));
       }
     }
   }
   if (!hasUploadOrRemove) {
     actions.add(new Uninstall());
   }
   return actions;
 }
 public boolean hasUploadableSites() {
   for (final UpdateSite site : updateSites.values())
     if (site.isActive() && site.isUploadable()) return true;
   return false;
 }