Guest User

Untitled

a guest
Mar 23rd, 2015
252
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 26.48 KB | None | 0 0
  1. package org.micromanager.internal.utils;
  2.  
  3. import com.google.common.io.BaseEncoding;
  4.  
  5. import java.io.ByteArrayInputStream;
  6. import java.io.ByteArrayOutputStream;
  7. import java.io.File;
  8. import java.io.FileNotFoundException;
  9. import java.io.FileWriter;
  10. import java.io.IOException;
  11. import java.io.ObjectInputStream;
  12. import java.io.ObjectOutputStream;
  13. import java.io.Serializable;
  14. import java.util.HashMap;
  15. import java.util.HashSet;
  16. import java.util.regex.Matcher;
  17. import java.util.regex.Pattern;
  18. import java.util.Scanner;
  19. import java.util.Set;
  20.  
  21. import org.json.JSONException;
  22. import org.json.JSONObject;
  23.  
  24. import org.micromanager.PropertyMap;
  25. import org.micromanager.UserProfile;
  26. import org.micromanager.data.internal.DefaultPropertyMap;
  27. import org.micromanager.internal.utils.MDUtils;
  28.  
  29. public class DefaultUserProfile implements UserProfile {
  30.    private static final String USERNAME_MAPPING_FILE = "Profiles.txt";
  31.    private static final String GLOBAL_USER = "Global defaults";
  32.    public static final String DEFAULT_USER = "Default user";
  33.    private static final String ALWAYS_USE_DEFAULT_USER = "always use the default user profile";
  34.  
  35.    private static final DefaultUserProfile staticInstance_;
  36.    static {
  37.       staticInstance_ = new DefaultUserProfile();
  38.    }
  39.    private HashMap<String, String> nameToFile_;
  40.    private String profileName_;
  41.    private final DefaultPropertyMap globalProfile_;
  42.    private DefaultPropertyMap userProfile_;
  43.    // This object exists to give us something consistent to lock on.
  44.    private static final Object lockObject_ = new Object();
  45.    // This thread will be periodically checking if it should save.
  46.    private Thread saveThread_;
  47.    private static final Object syncObject_ = new Object();
  48.    private IOException saveException_;
  49.  
  50.    public DefaultUserProfile() {
  51.       nameToFile_ = loadProfileMapping();
  52.       String globalPath = new File(UserProfile.GLOBAL_SETTINGS_FILE).getAbsolutePath();
  53.       globalProfile_ = loadPropertyMap(globalPath);
  54.       // Naturally we start with the default user loaded.
  55.       setCurrentProfile(DEFAULT_USER);
  56.       startSaveThread();
  57.    }
  58.  
  59.    /**
  60.     * Read the username mapping file so we know which user's profile is in
  61.     * which file.
  62.     */
  63.    private HashMap<String, String> loadProfileMapping() {
  64.       JSONObject mapping = new JSONObject();
  65.       HashMap<String, String> result = new HashMap<String, String>();
  66.       File tmp = new File(JavaUtils.getApplicationDataPath() +
  67.             "/" + USERNAME_MAPPING_FILE);
  68.       if (!tmp.exists()) {
  69.          ReportingUtils.logMessage("Creating user profile mapping file");
  70.          // No file to be found; create it with no entry.
  71.          writeProfileMapping(result);
  72.       }
  73.       try {
  74.          mapping = new JSONObject(
  75.                loadFileToString(JavaUtils.getApplicationDataPath() +
  76.                   "/" + USERNAME_MAPPING_FILE));
  77.       }
  78.       catch (JSONException e) {
  79.          ReportingUtils.logError(e, "Unable to load user profiles mapping file");
  80.       }
  81.       for (String key : MDUtils.getKeys(mapping)) {
  82.          try {
  83.             result.put(key, mapping.getString(key));
  84.          }
  85.          catch (JSONException e) {
  86.             ReportingUtils.logError(e, "Unable to get filename for user " + key);
  87.          }
  88.       }
  89.       return result;
  90.    }
  91.  
  92.    /**
  93.     * Write out the current mapping of usernames to profile files.
  94.     */
  95.    private void writeProfileMapping(HashMap<String, String> nameToFile) {
  96.       JSONObject mapping = new JSONObject();
  97.       for (String name : nameToFile.keySet()) {
  98.          try {
  99.             mapping.put(name, nameToFile.get(name));
  100.          }
  101.          catch (JSONException e) {
  102.             ReportingUtils.logError(e, "Unable to add entry for profile " + name + " to file " + nameToFile.get(name));
  103.          }
  104.       }
  105.  
  106.       JavaUtils.createApplicationDataPathIfNeeded();
  107.       try {
  108.          FileWriter writer = new FileWriter(
  109.                JavaUtils.getApplicationDataPath() + "/" + USERNAME_MAPPING_FILE);
  110.          writer.write(mapping.toString(2) + "\n");
  111.          writer.close();
  112.       }
  113.       catch (FileNotFoundException e) {
  114.          ReportingUtils.logError(e, "Unable to open writer to save user profile mapping file");
  115.       }
  116.       catch (JSONException e) {
  117.          ReportingUtils.logError(e, "Unable to convert JSON mapping into string");
  118.       }
  119.       catch (IOException e) {
  120.          ReportingUtils.logError(e, "Exception when writing user profile mapping file");
  121.       }
  122.    }
  123.  
  124.    /**
  125.     * Load a PropertyMap for a given user; return an empty PropertyMap if that
  126.     * user doesn't yet exist, creating the profile for them in the process.
  127.     */
  128.    private DefaultPropertyMap loadProfile(String profileName) {
  129.       if (!nameToFile_.containsKey(profileName)) {
  130.          // Create a new profile.
  131.          ReportingUtils.logMessage("Profile name " + profileName + " not found in profile mapping; creating that user");
  132.          addProfile(profileName);
  133.          return (DefaultPropertyMap) (new DefaultPropertyMap.Builder().build());
  134.       }
  135.       String filename = nameToFile_.get(profileName);
  136.       JavaUtils.createApplicationDataPathIfNeeded();
  137.       String path = JavaUtils.getApplicationDataPath() + "/" + filename;
  138.       return loadPropertyMap(path);
  139.    }
  140.  
  141.    private DefaultPropertyMap loadPropertyMap(String path) {
  142.       String contents = loadFileToString(path);
  143.       if (contents == null) {
  144.          // That file doesn't exist yet; create a new empty user.
  145.          ReportingUtils.logMessage("Asked to load file at path " + path +
  146.                " which doesn't exist; instead providing a default empty PropertyMap.");
  147.          return (DefaultPropertyMap) (new DefaultPropertyMap.Builder().build());
  148.       }
  149.       try {
  150.          return DefaultPropertyMap.fromJSON(
  151.                new JSONObject(loadFileToString(path)));
  152.       }
  153.       catch (JSONException e) {
  154.          ReportingUtils.showError(e, "There was an error when loading saved user preferences. Please delete the file at " + path + " and re-start Micro-Manager.");
  155.          return null;
  156.       }
  157.    }
  158.  
  159.    /**
  160.     * Load the contents of a file and return them as a string. Adapted from
  161.     * http://stackoverflow.com/questions/326390/how-to-create-a-java-string-from-the-contents-of-a-file
  162.     * TODO: should probably be moved into utils somewhere.
  163.     */
  164.    private String loadFileToString(String path) {
  165.       File propFile = new File(path);
  166.       if (!propFile.exists()) {
  167.          return null;
  168.       }
  169.       StringBuilder contents = new StringBuilder((int) propFile.length());
  170.       Scanner scanner;
  171.       try {
  172.          scanner = new Scanner(propFile);
  173.       }
  174.       catch (FileNotFoundException e) {
  175.          // This should never happen since we checked if file exists.
  176.          ReportingUtils.logError(e,
  177.                "Somehow failed to open scanner for file at " + path);
  178.          return null;
  179.       }
  180.       String separator = System.getProperty("line.separator");
  181.       try {
  182.          while (scanner.hasNextLine()) {
  183.             contents.append(scanner.nextLine() + separator);
  184.          }
  185.       }
  186.       finally {
  187.          scanner.close();
  188.       }
  189.       return contents.toString();
  190.    }
  191.  
  192.    private void startSaveThread() {
  193.       saveThread_ = new Thread() {
  194.          @Override
  195.          public void run() {
  196.             runSaveThread();
  197.          }
  198.       };
  199.       saveThread_.start();
  200.    }
  201.  
  202.    /**
  203.     * Periodically check for changes in the profile (by seeing if this method's
  204.     * copy of the userProfile_ reference is different from the current
  205.     * reference), and save the profile to disk after a) a change has occurred,
  206.     * and b) it has been at least 5s since the change.
  207.     * We also can be interrupted and forced to save immediately, in which
  208.     * case we immediately write to disk, set the saveException_ exception if
  209.     * an exception occurred, and then exit (we'll be restarted immediately
  210.     * afterwards; check the syncToDisk() method).
  211.     */
  212.    private void runSaveThread() {
  213.       PropertyMap curRef = userProfile_;
  214.       boolean wasInterrupted = false;
  215.       long targetSaveTime = 0;
  216.       boolean haveLoggedFailure = false;
  217.       while (true) {
  218.          try {
  219.             Thread.sleep(100);
  220.          }
  221.          catch (InterruptedException e) {
  222.             wasInterrupted = true;
  223.          }
  224.          if (wasInterrupted || Thread.interrupted() ||
  225.                (targetSaveTime != 0 && System.currentTimeMillis() > targetSaveTime)) {
  226.             // Either enough time has passed or we were interrupted; time to
  227.             // save the profile.
  228.             JavaUtils.createApplicationDataPathIfNeeded();
  229.             try {
  230.                exportProfileToFile(JavaUtils.getApplicationDataPath() +
  231.                      "/" + nameToFile_.get(profileName_));
  232.             }
  233.             catch (IOException e) {
  234.                if (wasInterrupted) {
  235.                   // Record the exception for our "caller"
  236.                   saveException_ = e;
  237.                }
  238.                else if (!haveLoggedFailure) {
  239.                   // Log write failures once per thread, so a) errors aren't
  240.                   // silently swallowed, but b) we don't spam the logs.
  241.                   ReportingUtils.logError(e,
  242.                         "Failed to sync user profile to disk. Further logging of this error will be suppressed.");
  243.                   haveLoggedFailure = true;
  244.                }
  245.             }
  246.             if (wasInterrupted) {
  247.                // Now time to exit.
  248.                return;
  249.             }
  250.             targetSaveTime = 0;
  251.          }
  252.          // Check for changes in the profile, and if the profile changes,
  253.          // start/update the countdown to when we should save.
  254.          if (userProfile_ != curRef) {
  255.             targetSaveTime = System.currentTimeMillis() + 5000;
  256.             curRef = userProfile_;
  257.          }
  258.       }
  259.    }
  260.  
  261.    /**
  262.     * Append a class name to the provided key to generate a unique-across-MM
  263.     * key.
  264.     * Note that exportProfileSubsetToFile() and clearProfileSubset() assume that
  265.     * keys start with the class' canonical name.
  266.     */
  267.    private String genKey(Class<?> c, String key) {
  268.       return c.getCanonicalName() + ":" + key;
  269.    }
  270.  
  271.    // Getters/setters. There is a ton of duplication here; unfortunately, I
  272.    // don't see any real way to avoid it. Here's hoping that my auto-generated
  273.    // code doesn't have any typos in it!
  274.  
  275.    @Override
  276.    public String getString(Class<?> c, String key, String fallback) {
  277.       key = genKey(c, key);
  278.       synchronized(lockObject_) {
  279.          String result = userProfile_.getString(key);
  280.          if (result != null) {
  281.             return result;
  282.          }
  283.       }
  284.       // Try the global profile.
  285.       synchronized(globalProfile_) {
  286.          String result = globalProfile_.getString(key);
  287.          if (result != null) {
  288.             return result;
  289.          }
  290.       }
  291.       // Give up.
  292.       return fallback;
  293.    }
  294.    @Override
  295.    public String[] getStringArray(Class<?> c, String key, String[] fallback) {
  296.       key = genKey(c, key);
  297.       synchronized(lockObject_) {
  298.          String[] result = userProfile_.getStringArray(key);
  299.          if (result != null) {
  300.             return result;
  301.          }
  302.       }
  303.       // Try the global profile.
  304.       synchronized(globalProfile_) {
  305.          String[] result = globalProfile_.getStringArray(key);
  306.          if (result != null) {
  307.             return result;
  308.          }
  309.       }
  310.       // Give up.
  311.       return fallback;
  312.    }
  313.    @Override
  314.    public void setString(Class<?> c, String key, String value) {
  315.       synchronized(lockObject_) {
  316.          userProfile_ = (DefaultPropertyMap) userProfile_.copy().putString(genKey(c, key), value).build();
  317.       }
  318.    }
  319.    @Override
  320.    public void setStringArray(Class<?> c, String key, String[] value) {
  321.       synchronized(lockObject_) {
  322.          userProfile_ = (DefaultPropertyMap) userProfile_.copy().putStringArray(genKey(c, key), value).build();
  323.       }
  324.    }
  325.  
  326.    @Override
  327.    public Integer getInt(Class<?> c, String key, Integer fallback) {
  328.       key = genKey(c, key);
  329.       synchronized(lockObject_) {
  330.          Integer result = userProfile_.getInt(key);
  331.          if (result != null) {
  332.             return result;
  333.          }
  334.       }
  335.       // Try the global profile.
  336.       synchronized(globalProfile_) {
  337.          Integer result = globalProfile_.getInt(key);
  338.          if (result != null) {
  339.             return result;
  340.          }
  341.       }
  342.       // Give up.
  343.       return fallback;
  344.    }
  345.    @Override
  346.    public Integer[] getIntArray(Class<?> c, String key, Integer[] fallback) {
  347.       key = genKey(c, key);
  348.       synchronized(lockObject_) {
  349.          Integer[] result = userProfile_.getIntArray(key);
  350.          if (result != null) {
  351.             return result;
  352.          }
  353.       }
  354.       // Try the global profile.
  355.       synchronized(globalProfile_) {
  356.          Integer[] result = globalProfile_.getIntArray(key);
  357.          if (result != null) {
  358.             return result;
  359.          }
  360.       }
  361.       // Give up.
  362.       return fallback;
  363.    }
  364.    @Override
  365.    public void setInt(Class<?> c, String key, Integer value) {
  366.       synchronized(lockObject_) {
  367.          userProfile_ = (DefaultPropertyMap) userProfile_.copy().putInt(genKey(c, key), value).build();
  368.       }
  369.    }
  370.    @Override
  371.    public void setIntArray(Class<?> c, String key, Integer[] value) {
  372.       synchronized(lockObject_) {
  373.          userProfile_ = (DefaultPropertyMap) userProfile_.copy().putIntArray(genKey(c, key), value).build();
  374.       }
  375.    }
  376.  
  377.    @Override
  378.    public Long getLong(Class<?> c, String key, Long fallback) {
  379.       key = genKey(c, key);
  380.       synchronized(lockObject_) {
  381.          Long result = userProfile_.getLong(key);
  382.          if (result != null) {
  383.             return result;
  384.          }
  385.       }
  386.       // Try the global profile.
  387.       synchronized(globalProfile_) {
  388.          Long result = globalProfile_.getLong(key);
  389.          if (result != null) {
  390.             return result;
  391.          }
  392.       }
  393.       // Give up.
  394.       return fallback;
  395.    }
  396.    @Override
  397.    public Long[] getLongArray(Class<?> c, String key, Long[] fallback) {
  398.       key = genKey(c, key);
  399.       synchronized(lockObject_) {
  400.          Long[] result = userProfile_.getLongArray(key);
  401.          if (result != null) {
  402.             return result;
  403.          }
  404.       }
  405.       // Try the global profile.
  406.       synchronized(globalProfile_) {
  407.          Long[] result = globalProfile_.getLongArray(key);
  408.          if (result != null) {
  409.             return result;
  410.          }
  411.       }
  412.       // Give up.
  413.       return fallback;
  414.    }
  415.    @Override
  416.    public void setLong(Class<?> c, String key, Long value) {
  417.       synchronized(lockObject_) {
  418.          userProfile_ = (DefaultPropertyMap) userProfile_.copy().putLong(genKey(c, key), value).build();
  419.       }
  420.    }
  421.    @Override
  422.    public void setLongArray(Class<?> c, String key, Long[] value) {
  423.       synchronized(lockObject_) {
  424.          userProfile_ = (DefaultPropertyMap) userProfile_.copy().putLongArray(genKey(c, key), value).build();
  425.       }
  426.    }
  427.  
  428.    @Override
  429.    public Double getDouble(Class<?> c, String key, Double fallback) {
  430.       key = genKey(c, key);
  431.       synchronized(lockObject_) {
  432.          Double result = userProfile_.getDouble(key);
  433.          if (result != null) {
  434.             return result;
  435.          }
  436.       }
  437.       // Try the global profile.
  438.       synchronized(globalProfile_) {
  439.          Double result = globalProfile_.getDouble(key);
  440.          if (result != null) {
  441.             return result;
  442.          }
  443.       }
  444.       // Give up.
  445.       return fallback;
  446.    }
  447.    @Override
  448.    public Double[] getDoubleArray(Class<?> c, String key, Double[] fallback) {
  449.       key = genKey(c, key);
  450.       synchronized(lockObject_) {
  451.          Double[] result = userProfile_.getDoubleArray(key);
  452.          if (result != null) {
  453.             return result;
  454.          }
  455.       }
  456.       // Try the global profile.
  457.       synchronized(globalProfile_) {
  458.          Double[] result = globalProfile_.getDoubleArray(key);
  459.          if (result != null) {
  460.             return result;
  461.          }
  462.       }
  463.       // Give up.
  464.       return fallback;
  465.    }
  466.    @Override
  467.    public void setDouble(Class<?> c, String key, Double value) {
  468.       synchronized(lockObject_) {
  469.          userProfile_ = (DefaultPropertyMap) userProfile_.copy().putDouble(genKey(c, key), value).build();
  470.       }
  471.    }
  472.    @Override
  473.    public void setDoubleArray(Class<?> c, String key, Double[] value) {
  474.       synchronized(lockObject_) {
  475.          userProfile_ = (DefaultPropertyMap) userProfile_.copy().putDoubleArray(genKey(c, key), value).build();
  476.       }
  477.    }
  478.  
  479.    @Override
  480.    public Boolean getBoolean(Class<?> c, String key, Boolean fallback) {
  481.       key = genKey(c, key);
  482.       synchronized(lockObject_) {
  483.          Boolean result = userProfile_.getBoolean(key);
  484.          if (result != null) {
  485.             return result;
  486.          }
  487.       }
  488.       // Try the global profile.
  489.       synchronized(globalProfile_) {
  490.          Boolean result = globalProfile_.getBoolean(key);
  491.          if (result != null) {
  492.             return result;
  493.          }
  494.       }
  495.       // Give up.
  496.       return fallback;
  497.    }
  498.    @Override
  499.    public Boolean[] getBooleanArray(Class<?> c, String key, Boolean[] fallback) {
  500.       key = genKey(c, key);
  501.       synchronized(lockObject_) {
  502.          Boolean[] result = userProfile_.getBooleanArray(key);
  503.          if (result != null) {
  504.             return result;
  505.          }
  506.       }
  507.       // Try the global profile.
  508.       synchronized(globalProfile_) {
  509.          Boolean[] result = globalProfile_.getBooleanArray(key);
  510.          if (result != null) {
  511.             return result;
  512.          }
  513.       }
  514.       // Give up.
  515.       return fallback;
  516.    }
  517.    @Override
  518.    public void setBoolean(Class<?> c, String key, Boolean value) {
  519.       synchronized(lockObject_) {
  520.          userProfile_ = (DefaultPropertyMap) userProfile_.copy().putBoolean(genKey(c, key), value).build();
  521.       }
  522.    }
  523.    @Override
  524.    public void setBooleanArray(Class<?> c, String key, Boolean[] value) {
  525.       synchronized(lockObject_) {
  526.          userProfile_ = (DefaultPropertyMap) userProfile_.copy().putBooleanArray(genKey(c, key), value).build();
  527.       }
  528.    }
  529.  
  530.    /**
  531.     * Adapted from the old JavaUtils.getObjectFromPrefs() method, which was
  532.     * removed when we moved over to the Profile-based system. We store
  533.     * objects as base64-encoded Strings, so we have to reverse that process
  534.     * to retrieve the original Object.
  535.     */
  536.    @Override
  537.    public Object getObject(Class<?> c, String key, Object fallback) throws IOException {
  538.       String encoded = getString(c, key, null);
  539.       if (encoded == null) {
  540.          return fallback;
  541.       }
  542.       byte[] bytes = BaseEncoding.base64().decode(encoded);
  543.       ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
  544.       ObjectInputStream objectStream = new ObjectInputStream(byteStream);
  545.       try {
  546.          return objectStream.readObject();
  547.       }
  548.       catch (ClassNotFoundException e) {
  549.          throw new IOException(e);
  550.       }
  551.    }
  552.  
  553.    /**
  554.     * Adapted from the old JavaUtils.putObjectInPrefs() method, which was
  555.     * removed when we moved over to the Profile-based system. We convert the
  556.     * Object into a base64-encoded String and store it that way.
  557.     */
  558.    @Override
  559.    public void setObject(Class<?> c, String key, Serializable value) throws IOException {
  560.       ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
  561.       try {
  562.          ObjectOutputStream objectStream = new ObjectOutputStream(byteStream);
  563.          objectStream.writeObject(value);
  564.       } catch (Exception e) {
  565.          throw new IOException("Failed to serialize object");
  566.       }
  567.       String encoded = BaseEncoding.base64().encode(byteStream.toByteArray());
  568.       setString(c, key, encoded);
  569.    }
  570.  
  571.    @Override
  572.    public void exportProfileSubsetToFile(Class<?> c, String path) throws IOException {
  573.       // Make a copy profile, and save that.
  574.       DefaultPropertyMap.Builder builder = new DefaultPropertyMap.Builder();
  575.       // NOTE: this should match genKey()
  576.       String className = c.getCanonicalName();
  577.       synchronized(lockObject_) {
  578.          for (String key : userProfile_.getKeys()) {
  579.             if (key.startsWith(className)) {
  580.                builder.putProperty(key, userProfile_.getProperty(key));
  581.             }
  582.          }
  583.       }
  584.       exportPropertyMapToFile((DefaultPropertyMap) builder.build(), path);
  585.    }
  586.  
  587.    @Override
  588.    public void syncToDisk() throws IOException {
  589.       // Only one caller can invoke this method at a time.
  590.       synchronized(syncObject_) {
  591.          saveException_ = null;
  592.          saveThread_.interrupt();
  593.          // Wait for saving to complete, indicated by the thread exiting.
  594.          try {
  595.             saveThread_.join();
  596.          }
  597.          catch (InterruptedException e) {
  598.             // This should never happen.
  599.             ReportingUtils.logError(e, "Interrupted while waiting for sync-to-disk to complete");
  600.          }
  601.          // Now re-start the thread.
  602.          startSaveThread();
  603.          if (saveException_ != null) {
  604.             // Something went wrong while saving.
  605.             throw(saveException_);
  606.          }
  607.       }
  608.    }
  609.  
  610.    @Override
  611.    public void exportProfileToFile(String path) throws IOException {
  612.       exportPropertyMapToFile(userProfile_, path);
  613.    }
  614.  
  615.    @Override
  616.    public void exportCombinedProfileToFile(String path) throws IOException {
  617.       exportPropertyMapToFile(
  618.             (DefaultPropertyMap) (userProfile_.merge(globalProfile_)), path);
  619.    }
  620.  
  621.    private void exportPropertyMapToFile(DefaultPropertyMap properties, String path) throws IOException {
  622.       JSONObject serialization;
  623.       synchronized(properties) {
  624.          serialization = properties.toJSON();
  625.       }
  626.       try {
  627.          FileWriter writer = new FileWriter(path);
  628.          writer.write(serialization.toString(2) + "\n");
  629.          writer.close();
  630.       }
  631.       catch (FileNotFoundException e) {
  632.          ReportingUtils.logError(e, "Unable to open writer to save user profile mapping file");
  633.       }
  634.       catch (JSONException e) {
  635.          ReportingUtils.logError(e, "Unable to convert JSON mapping into string");
  636.       }
  637.    }
  638.  
  639.    @Override
  640.    public void appendFile(String path) {
  641.       File file = new File(path);
  642.       if (!file.exists()) {
  643.          ReportingUtils.logError("Asked to load file at " + path + " that does not exist.");
  644.          return;
  645.       }
  646.       PropertyMap properties = loadPropertyMap(path);
  647.       userProfile_ = (DefaultPropertyMap) userProfile_.merge(properties);
  648.    }
  649.  
  650.    public Set<String> getProfileNames() {
  651.       // HACK: don't reveal the global profile since it's not technically a
  652.       // "user".
  653.       HashSet<String> result = new HashSet<String>(nameToFile_.keySet());
  654.       result.remove(GLOBAL_USER);
  655.       return result;
  656.    }
  657.  
  658.    public void addProfile(String profileName) {
  659.       // Assign a filename for the user, which should be 1 greater than the
  660.       // largest current numerical filename we're using.
  661.       Pattern pattern = Pattern.compile("profile-(\\d+).txt");
  662.       int fileIndex = 0;
  663.       for (String key : nameToFile_.keySet()) {
  664.          String fileName = nameToFile_.get(key);
  665.          Matcher matcher = pattern.matcher(fileName);
  666.          if (!matcher.matches()) {
  667.             continue;
  668.          }
  669.          try {
  670.             int index = Integer.parseInt(matcher.group(1));
  671.             fileIndex = Math.max(index, fileIndex);
  672.          }
  673.          catch (NumberFormatException e) {
  674.             // No number to use.
  675.             continue;
  676.          }
  677.       }
  678.       String newFile = "profile-" + (fileIndex + 1) + ".txt";
  679.       nameToFile_.put(profileName, newFile);
  680.       writeProfileMapping(nameToFile_);
  681.    }
  682.  
  683.    public void setCurrentProfile(String profileName) {
  684.       profileName_ = profileName;
  685.       userProfile_ = loadProfile(profileName);
  686.    }
  687.  
  688.    public String getProfileName() {
  689.       return profileName_;
  690.    }
  691.  
  692.    @Override
  693.    public void clearProfileSubset(Class<?> c) {
  694.       // Make a copy of the map that contains everything except the keys for
  695.       // the specified class.
  696.       synchronized(lockObject_) {
  697.          DefaultPropertyMap.Builder builder = new DefaultPropertyMap.Builder();
  698.          for (String key : userProfile_.getKeys()) {
  699.             if (!key.startsWith(c.getCanonicalName())) {
  700.                builder.putProperty(key, userProfile_.getProperty(key));
  701.             }
  702.          }
  703.          userProfile_ = (DefaultPropertyMap) builder.build();
  704.       }
  705.    }
  706.  
  707.    /**
  708.     * Delete all parameters for the current user.
  709.     */
  710.    public void clearProfile() {
  711.       userProfile_ = (DefaultPropertyMap) (new DefaultPropertyMap.Builder().build());
  712.    }
  713.  
  714.    public static DefaultUserProfile getInstance() {
  715.       return staticInstance_;
  716.    }
  717.  
  718.    /**
  719.     * This special property controls whether or not we always use the
  720.     * "Default user" profile.
  721.     */
  722.    public static boolean getShouldAlwaysUseDefaultProfile() {
  723.       DefaultUserProfile profile = getInstance();
  724.       // This parameter is stored for the default user. Just in case we *are*
  725.       // using the default user, wrap this in synchronized.
  726.       synchronized(lockObject_) {
  727.          PropertyMap defaultProfile = profile.loadProfile(DEFAULT_USER);
  728.          Boolean result = defaultProfile.getBoolean(
  729.                profile.genKey(DefaultUserProfile.class, ALWAYS_USE_DEFAULT_USER));
  730.          if (result == null) {
  731.             return false;
  732.          }
  733.          return result;
  734.       }
  735.    }
  736.  
  737.    /**
  738.     * As above, but set the value. It will only take effect after a restart.
  739.     */
  740.    public static void setShouldAlwaysUseDefaultProfile(boolean shouldUseDefault) {
  741.       DefaultUserProfile profile = getInstance();
  742.       // Again, in case we're using the default user already, we wrap this
  743.       // in synchronized.
  744.       synchronized(lockObject_) {
  745.          // In order to simplify saving things (since syncToDisk() always
  746.          // saves the current user), we temporarily switch to the
  747.          // default user for this operation.
  748.          String curProfile = profile.profileName_;
  749.          profile.setCurrentProfile(DEFAULT_USER);
  750.          profile.setBoolean(DefaultUserProfile.class,
  751.                ALWAYS_USE_DEFAULT_USER, shouldUseDefault);
  752.          try {
  753.             profile.syncToDisk();
  754.          }
  755.          catch (IOException e) {
  756.             ReportingUtils.logError(e, "Unable to set whether default user should be used");
  757.          }
  758.          profile.setCurrentProfile(curProfile);
  759.       }
  760.    }
  761. }
Add Comment
Please, Sign In to add comment