private void exportParameters() {
    synchronized (exported_parameters) {
      if (!exported_parameters_dirty) {

        return;
      }

      exported_parameters_dirty = false;

      try {
        TreeMap<String, String> tm = new TreeMap<String, String>();

        Set<String> exported_keys = new HashSet<String>();

        for (String[] entry : exported_parameters.values()) {

          String key = entry[0];
          String value = entry[1];

          exported_keys.add(key);

          if (value != null) {

            tm.put(key, value);
          }
        }

        for (Map.Entry<String, String> entry : imported_parameters.entrySet()) {

          String key = entry.getKey();

          if (!exported_keys.contains(key)) {

            tm.put(key, entry.getValue());
          }
        }

        File parent_dir = new File(SystemProperties.getUserPath());

        File props = new File(parent_dir, "exported_params.properties");

        PrintWriter pw =
            new PrintWriter(new OutputStreamWriter(new FileOutputStream(props), "UTF-8"));

        try {
          for (Map.Entry<String, String> entry : tm.entrySet()) {

            pw.println(entry.getKey() + "=" + entry.getValue());
          }

        } finally {

          pw.close();
        }
      } catch (Throwable e) {

        e.printStackTrace();
      }
    }
  }
  public void save(String filename) {
    if (propertiesMap == null) {

      // nothing to save, initialisation not complete

      return;
    }

    /**
     * Note - propertiesMap isn't synchronised! We'll clone the map now, because we need to modify
     * it. The BEncoding code will create a new map object (TreeMap) because it needs to be sorted,
     * so we might as well do it here too.
     */
    TreeMap<String, Object> properties_clone = propertiesMap.toTreeMap();

    // Remove any transient parameters.
    if (!this.transient_properties.isEmpty()) {
      properties_clone.keySet().removeAll(this.transient_properties);
    }

    FileUtil.writeResilientConfigFile(filename, properties_clone);

    List<COConfigurationListener> listeners_copy;

    synchronized (listenerz) {
      listeners_copy = new ArrayList<COConfigurationListener>(listenerz);
    }

    for (int i = 0; i < listeners_copy.size(); i++) {

      COConfigurationListener l = (COConfigurationListener) listeners_copy.get(i);

      if (l != null) {

        try {
          l.configurationSaved();

        } catch (Throwable e) {

          Debug.printStackTrace(e);
        }
      } else {

        Debug.out("COConfigurationListener is null");
      }
    }

    if (exported_parameters_dirty) {

      exportParameters();
    }
  }
  // Used for debugging in main.
  private static void printDataMap(Map map) throws Exception {
    TreeMap res = new TreeMap(map);
    Iterator key_itr = map.keySet().iterator();
    while (key_itr.hasNext()) {
      Object key = key_itr.next();
      Object val = map.get(key);
      if (val instanceof byte[]) {
        String as_bytes = ByteFormatter.nicePrint((byte[]) val);
        String as_text = new String((byte[]) val, Constants.BYTE_ENCODING);
        res.put(key, as_text + " [" + as_bytes + "]");
      }
    }

    Iterator entries = res.entrySet().iterator();
    Map.Entry entry;
    while (entries.hasNext()) {
      entry = (Map.Entry) entries.next();
      System.out.print("  ");
      System.out.print(entry.getKey());
      System.out.print(": ");
      System.out.print(entry.getValue());
      System.out.println();
    }
  }
  public static licenceDetails getFullFeatureDetails() {
    if (featman == null) {
      Debug.out("featman null");
      return null;
    }

    TreeMap<Long, Object[]> mapOrder = new TreeMap<Long, Object[]>(Collections.reverseOrder());
    FeatureDetails[] featureDetails = featman.getFeatureDetails("dvdburn");
    // if any of the feature details are still valid, we have a full
    for (FeatureDetails fd : featureDetails) {
      Licence licence = fd.getLicence();
      int state = licence.getState();
      if (state == Licence.LS_ACTIVATION_DENIED) {
        mapOrder.put(-1L, new Object[] {licence, Long.valueOf(0)});
        continue;
      } else if (state == Licence.LS_CANCELLED) {
        mapOrder.put(-2L, new Object[] {licence, Long.valueOf(0)});
        continue;
      } else if (state == Licence.LS_INVALID_KEY) {
        mapOrder.put(-3L, new Object[] {licence, Long.valueOf(0)});
        continue;
      } else if (state == Licence.LS_REVOKED) {
        mapOrder.put(-4L, new Object[] {licence, Long.valueOf(0)});
        continue;
      } else if (state == Licence.LS_PENDING_AUTHENTICATION) {
        mapOrder.put(-6L, new Object[] {licence, Long.valueOf(0)});
        continue;
      }

      long now = SystemTime.getCurrentTime();
      Long lValidUntil = (Long) fd.getProperty(FeatureDetails.PR_VALID_UNTIL);
      Long lValidOfflineUntil = (Long) fd.getProperty(FeatureDetails.PR_OFFLINE_VALID_UNTIL);

      if (lValidUntil == null && lValidOfflineUntil == null) {
        continue;
      }

      long minValidUntil = -1;
      long maxValidUntil = -1;
      if (lValidUntil != null) {
        minValidUntil = maxValidUntil = lValidUntil.longValue();
        if (minValidUntil < now) {
          mapOrder.put(minValidUntil, new Object[] {licence, Long.valueOf(minValidUntil)});
          continue;
        }
      }
      if (lValidOfflineUntil != null) {
        long validOfflineUntil = lValidOfflineUntil.longValue();
        if (validOfflineUntil < now) {
          mapOrder.put(validOfflineUntil, new Object[] {licence, Long.valueOf(maxValidUntil)});
          continue;
        }
        if (maxValidUntil == -1 || validOfflineUntil > maxValidUntil) {
          maxValidUntil = validOfflineUntil;
        }
      }

      mapOrder.put(maxValidUntil, new Object[] {licence, minValidUntil});
    }

    if (mapOrder.size() == 0) {
      return null;
    }

    Long firstKey = mapOrder.firstKey();
    Object[] objects = mapOrder.get(firstKey);
    Licence licence = (Licence) objects[0];
    return new licenceDetails(firstKey.longValue(), ((Long) objects[1]).longValue(), licence);
  }