12dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll/*
22dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll * Copyright (C) 2012 The Android Open Source Project
32dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll *
42dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll * Licensed under the Apache License, Version 2.0 (the "License");
52dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll * you may not use this file except in compliance with the License.
62dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll * You may obtain a copy of the License at
72dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll *
82dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll *      http://www.apache.org/licenses/LICENSE-2.0
92dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll *
102dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll * Unless required by applicable law or agreed to in writing, software
112dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll * distributed under the License is distributed on an "AS IS" BASIS,
122dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll * See the License for the specific language governing permissions and
142dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll * limitations under the License.
152dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll */
162dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
172dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Mollpackage com.android.sdklib.internal.repository.sources;
182dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
192dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Mollimport com.android.annotations.NonNull;
202dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Mollimport com.android.annotations.Nullable;
212dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Mollimport com.android.annotations.VisibleForTesting;
222dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Mollimport com.android.annotations.VisibleForTesting.Visibility;
232dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Mollimport com.android.prefs.AndroidLocation;
242dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Mollimport com.android.prefs.AndroidLocation.AndroidLocationException;
252dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
262dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Mollimport java.io.File;
272dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Mollimport java.io.FileInputStream;
282dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Mollimport java.io.FileOutputStream;
292dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Mollimport java.io.IOException;
302dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Mollimport java.util.Collections;
312dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Mollimport java.util.Comparator;
322dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Mollimport java.util.List;
332dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Mollimport java.util.Properties;
342dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
352dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll/**
362dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll * Properties for individual sources which are persisted by a local settings file.
372dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll * <p/>
382dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll * All instances of {@link SdkSourceProperties} share the same singleton storage.
392dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll * The persisted setting file is loaded as necessary, however callers must persist
402dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll * it at some point by calling {@link #save()}.
412dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll */
422dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Mollpublic class SdkSourceProperties {
432dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
442dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    /**
452dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * An internal file version number, in case we want to change the format later.
462dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     */
472dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    private static final String KEY_VERSION  = "@version@";                 //$NON-NLS-1$
482dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    /**
492dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * The last known UI name of the source.
502dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     */
512dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    public static final String KEY_NAME     = "@name@";                     //$NON-NLS-1$
522dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    /**
532dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * A non-null string if the source is disabled. Null if the source is enabled.
542dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     */
552dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    public static final String KEY_DISABLED = "@disabled@";                 //$NON-NLS-1$
562dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
572dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    private static final Properties sSourcesProperties = new Properties();
582dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    private static final String     SRC_FILENAME = "sites-settings.cfg";    //$NON-NLS-1$
592dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
602dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    private static boolean sModified = false;
612dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
622dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    public SdkSourceProperties() {
632dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    }
642dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
652dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    public void save() {
662dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        synchronized (sSourcesProperties) {
672dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            if (sModified && !sSourcesProperties.isEmpty()) {
682dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                saveLocked();
692dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                sModified = false;
702dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            }
712dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        }
722dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    }
732dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
742dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    /**
752dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * Retrieves a property for the given source URL and the given key type.
762dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * <p/>
772dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * Implementation detail: this loads the persistent settings file as needed.
782dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     *
792dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * @param key The kind of property to retrieve for that source URL.
802dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * @param sourceUrl The source URL.
812dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * @param defaultValue The default value to return, if the property isn't found. Can be null.
822dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * @return The non-null string property for the key/sourceUrl or the default value.
832dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     */
842dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    @Nullable
852dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    public String getProperty(@NonNull String key,
862dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                              @NonNull String sourceUrl,
872dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                              @Nullable String defaultValue) {
882dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        String value = defaultValue;
892dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
902dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        synchronized (sSourcesProperties) {
912dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            if (sSourcesProperties.isEmpty()) {
922dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                loadLocked();
932dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            }
942dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
952dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            value = sSourcesProperties.getProperty(key + sourceUrl, defaultValue);
962dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        }
972dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
982dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        return value;
992dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    }
1002dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
1012dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    /**
1022dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * Sets or remove a property for the given source URL and the given key type.
1032dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * <p/>
1042dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * Implementation detail: this does <em>not</em> save the persistent settings file.
1052dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * Somehow the caller will need to call the {@link #save()} method later.
1062dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     *
1072dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * @param key The kind of property to retrieve for that source URL.
1082dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * @param sourceUrl The source URL.
1092dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * @param value The new value to set (if non null) or null to remove an existing property.
1102dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     */
1112dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    public void setProperty(String key, String sourceUrl, String value) {
1122dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        synchronized (sSourcesProperties) {
1132dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            if (sSourcesProperties.isEmpty()) {
1142dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                loadLocked();
1152dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            }
1162dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
1172dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            key += sourceUrl;
1182dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
1192dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            String old = sSourcesProperties.getProperty(key);
1202dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            if (value == null) {
1212dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                if (old != null) {
1222dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                    sSourcesProperties.remove(key);
1232dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                    sModified = true;
1242dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                }
1252dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            } else if (old == null || !old.equals(value)) {
1262dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                sSourcesProperties.setProperty(key, value);
1272dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                sModified = true;
1282dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            }
1292dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        }
1302dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    }
1312dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
1322dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    /**
1332dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * Returns an internal string representation of the underlying Properties map,
1342dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * sorted by ascending keys. Useful for debugging and testing purposes only.
1352dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     */
1362dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    @Override
1372dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    public String toString() {
1382dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        StringBuilder sb = new StringBuilder("<SdkSourceProperties");      //$NON-NLS-1$
1392dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        synchronized (sSourcesProperties) {
1402dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            List<Object> keys = Collections.list(sSourcesProperties.keys());
1412dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            Collections.sort(keys, new Comparator<Object>() {
1422dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                @Override
1432dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                public int compare(Object o1, Object o2) {
1442dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                    return o1.toString().compareTo(o2.toString());
1452dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                }});
1462dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
1472dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            for (Object key : keys) {
1482dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                sb.append('\n').append(key)
1492dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                  .append(" = ").append(sSourcesProperties.get(key));       //$NON-NLS-1$
1502dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            }
1512dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        }
1522dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        sb.append('>');
1532dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        return sb.toString();
1542dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    }
1552dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
1562dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    /** Load state from persistent file. Expects sSourcesProperties to be synchronized. */
1572dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    private void loadLocked() {
1582dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        // Load state from persistent file
1592dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        if (loadProperties()) {
1602dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            // If it lacks our magic version key, don't use it
1612dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            if (sSourcesProperties.getProperty(KEY_VERSION) == null) {
1622dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                sSourcesProperties.clear();
1632dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            }
1642dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
1652dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            sModified = false;
1662dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        }
1672dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
1682dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        if (sSourcesProperties.isEmpty()) {
1692dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            // Nothing was loaded. Initialize the storage with a version
1702dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            // identified. This isn't currently checked back, but we might
1712dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            // want it later if we decide to change the way this works.
1722dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            // The version key is choosen on purpose to not match any valid URL.
1732dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            sSourcesProperties.setProperty(KEY_VERSION, "1"); //$NON-NLS-1$ //$NON-NLS-2$
1742dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        }
1752dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    }
1762dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
1772dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    /**
1782dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * Load properties from default file. Extracted so that it can be mocked in tests.
1792dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     *
1802dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * @return True if actually loaded the file. False if there was an IO error or no
1812dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     *   file and nothing was loaded.
1822dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     */
1832dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    @VisibleForTesting(visibility=Visibility.PRIVATE)
1842dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    protected boolean loadProperties() {
1852dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        try {
1862dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            String folder = AndroidLocation.getFolder();
1872dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            File f = new File(folder, SRC_FILENAME);
1882dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            if (f.exists()) {
1892dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                FileInputStream fis = null;
1902dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                try {
1912dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                    fis = new FileInputStream(f);
1922dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                    sSourcesProperties.load(fis);
1932dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                } catch (IOException ignore) {
1942dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                    // nop
1952dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                } finally {
1962dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                    if (fis != null) {
1972dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                        try {
1982dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                            fis.close();
1992dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                        } catch (IOException ignore) {}
2002dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                    }
2012dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                }
2022dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
2032dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                return true;
2042dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            }
2052dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        } catch (AndroidLocationException ignore) {
2062dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            // nop
2072dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        }
2082dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        return false;
2092dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    }
2102dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
2112dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    /**
2122dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * Save file to disk. Expects sSourcesProperties to be synchronized.
2132dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * Made accessible for testing purposes.
2142dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     * For public usage, please use {@link #save()} instead.
2152dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll     */
2162dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    @VisibleForTesting(visibility=Visibility.PRIVATE)
2172dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    protected void saveLocked() {
2182dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        // Persist it to the file
2192dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        FileOutputStream fos = null;
2202dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        try {
2212dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            String folder = AndroidLocation.getFolder();
2222dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            File f = new File(folder, SRC_FILENAME);
2232dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
2242dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            fos = new FileOutputStream(f);
2252dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
2262dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            sSourcesProperties.store(fos,"## Sites Settings for Android SDK Manager");//$NON-NLS-1$
2272dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
2282dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        } catch (AndroidLocationException ignore) {
2292dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            // nop
2302dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        } catch (IOException ignore) {
2312dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            // nop
2322dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        } finally {
2332dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            if (fos != null) {
2342dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                try {
2352dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                    fos.close();
2362dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll                } catch (IOException ignore) {}
2372dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            }
2382dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        }
2392dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    }
2402dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll
2412dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    /** Empty current property list. Made accessible for testing purposes. */
2422dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    @VisibleForTesting(visibility=Visibility.PRIVATE)
2432dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    protected void clear() {
2442dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        synchronized (sSourcesProperties) {
2452dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            sSourcesProperties.clear();
2462dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll            sModified = false;
2472dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll        }
2482dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll    }
2492dbb10456924ea2cbce7c4647881fd57f766c3aaRaphael Moll}
250