• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.sdklib.internal.repository.sources;
18 
19 import com.android.annotations.NonNull;
20 import com.android.annotations.Nullable;
21 import com.android.annotations.VisibleForTesting;
22 import com.android.annotations.VisibleForTesting.Visibility;
23 import com.android.prefs.AndroidLocation;
24 import com.android.prefs.AndroidLocation.AndroidLocationException;
25 
26 import java.io.File;
27 import java.io.FileInputStream;
28 import java.io.FileOutputStream;
29 import java.io.IOException;
30 import java.util.Collections;
31 import java.util.Comparator;
32 import java.util.List;
33 import java.util.Properties;
34 
35 /**
36  * Properties for individual sources which are persisted by a local settings file.
37  * <p/>
38  * All instances of {@link SdkSourceProperties} share the same singleton storage.
39  * The persisted setting file is loaded as necessary, however callers must persist
40  * it at some point by calling {@link #save()}.
41  */
42 public class SdkSourceProperties {
43 
44     /**
45      * An internal file version number, in case we want to change the format later.
46      */
47     private static final String KEY_VERSION  = "@version@";                 //$NON-NLS-1$
48     /**
49      * The last known UI name of the source.
50      */
51     public static final String KEY_NAME     = "@name@";                     //$NON-NLS-1$
52     /**
53      * A non-null string if the source is disabled. Null if the source is enabled.
54      */
55     public static final String KEY_DISABLED = "@disabled@";                 //$NON-NLS-1$
56 
57     private static final Properties sSourcesProperties = new Properties();
58     private static final String     SRC_FILENAME = "sites-settings.cfg";    //$NON-NLS-1$
59 
60     private static boolean sModified = false;
61 
SdkSourceProperties()62     public SdkSourceProperties() {
63     }
64 
save()65     public void save() {
66         synchronized (sSourcesProperties) {
67             if (sModified && !sSourcesProperties.isEmpty()) {
68                 saveLocked();
69                 sModified = false;
70             }
71         }
72     }
73 
74     /**
75      * Retrieves a property for the given source URL and the given key type.
76      * <p/>
77      * Implementation detail: this loads the persistent settings file as needed.
78      *
79      * @param key The kind of property to retrieve for that source URL.
80      * @param sourceUrl The source URL.
81      * @param defaultValue The default value to return, if the property isn't found. Can be null.
82      * @return The non-null string property for the key/sourceUrl or the default value.
83      */
84     @Nullable
getProperty(@onNull String key, @NonNull String sourceUrl, @Nullable String defaultValue)85     public String getProperty(@NonNull String key,
86                               @NonNull String sourceUrl,
87                               @Nullable String defaultValue) {
88         String value = defaultValue;
89 
90         synchronized (sSourcesProperties) {
91             if (sSourcesProperties.isEmpty()) {
92                 loadLocked();
93             }
94 
95             value = sSourcesProperties.getProperty(key + sourceUrl, defaultValue);
96         }
97 
98         return value;
99     }
100 
101     /**
102      * Sets or remove a property for the given source URL and the given key type.
103      * <p/>
104      * Implementation detail: this does <em>not</em> save the persistent settings file.
105      * Somehow the caller will need to call the {@link #save()} method later.
106      *
107      * @param key The kind of property to retrieve for that source URL.
108      * @param sourceUrl The source URL.
109      * @param value The new value to set (if non null) or null to remove an existing property.
110      */
setProperty(String key, String sourceUrl, String value)111     public void setProperty(String key, String sourceUrl, String value) {
112         synchronized (sSourcesProperties) {
113             if (sSourcesProperties.isEmpty()) {
114                 loadLocked();
115             }
116 
117             key += sourceUrl;
118 
119             String old = sSourcesProperties.getProperty(key);
120             if (value == null) {
121                 if (old != null) {
122                     sSourcesProperties.remove(key);
123                     sModified = true;
124                 }
125             } else if (old == null || !old.equals(value)) {
126                 sSourcesProperties.setProperty(key, value);
127                 sModified = true;
128             }
129         }
130     }
131 
132     /**
133      * Returns an internal string representation of the underlying Properties map,
134      * sorted by ascending keys. Useful for debugging and testing purposes only.
135      */
136     @Override
toString()137     public String toString() {
138         StringBuilder sb = new StringBuilder("<SdkSourceProperties");      //$NON-NLS-1$
139         synchronized (sSourcesProperties) {
140             List<Object> keys = Collections.list(sSourcesProperties.keys());
141             Collections.sort(keys, new Comparator<Object>() {
142                 @Override
143                 public int compare(Object o1, Object o2) {
144                     return o1.toString().compareTo(o2.toString());
145                 }});
146 
147             for (Object key : keys) {
148                 sb.append('\n').append(key)
149                   .append(" = ").append(sSourcesProperties.get(key));       //$NON-NLS-1$
150             }
151         }
152         sb.append('>');
153         return sb.toString();
154     }
155 
156     /** Load state from persistent file. Expects sSourcesProperties to be synchronized. */
loadLocked()157     private void loadLocked() {
158         // Load state from persistent file
159         if (loadProperties()) {
160             // If it lacks our magic version key, don't use it
161             if (sSourcesProperties.getProperty(KEY_VERSION) == null) {
162                 sSourcesProperties.clear();
163             }
164 
165             sModified = false;
166         }
167 
168         if (sSourcesProperties.isEmpty()) {
169             // Nothing was loaded. Initialize the storage with a version
170             // identified. This isn't currently checked back, but we might
171             // want it later if we decide to change the way this works.
172             // The version key is choosen on purpose to not match any valid URL.
173             sSourcesProperties.setProperty(KEY_VERSION, "1"); //$NON-NLS-1$ //$NON-NLS-2$
174         }
175     }
176 
177     /**
178      * Load properties from default file. Extracted so that it can be mocked in tests.
179      *
180      * @return True if actually loaded the file. False if there was an IO error or no
181      *   file and nothing was loaded.
182      */
183     @VisibleForTesting(visibility=Visibility.PRIVATE)
loadProperties()184     protected boolean loadProperties() {
185         try {
186             String folder = AndroidLocation.getFolder();
187             File f = new File(folder, SRC_FILENAME);
188             if (f.exists()) {
189                 FileInputStream fis = null;
190                 try {
191                     fis = new FileInputStream(f);
192                     sSourcesProperties.load(fis);
193                 } catch (IOException ignore) {
194                     // nop
195                 } finally {
196                     if (fis != null) {
197                         try {
198                             fis.close();
199                         } catch (IOException ignore) {}
200                     }
201                 }
202 
203                 return true;
204             }
205         } catch (AndroidLocationException ignore) {
206             // nop
207         }
208         return false;
209     }
210 
211     /**
212      * Save file to disk. Expects sSourcesProperties to be synchronized.
213      * Made accessible for testing purposes.
214      * For public usage, please use {@link #save()} instead.
215      */
216     @VisibleForTesting(visibility=Visibility.PRIVATE)
saveLocked()217     protected void saveLocked() {
218         // Persist it to the file
219         FileOutputStream fos = null;
220         try {
221             String folder = AndroidLocation.getFolder();
222             File f = new File(folder, SRC_FILENAME);
223 
224             fos = new FileOutputStream(f);
225 
226             sSourcesProperties.store(fos,"## Sites Settings for Android SDK Manager");//$NON-NLS-1$
227 
228         } catch (AndroidLocationException ignore) {
229             // nop
230         } catch (IOException ignore) {
231             // nop
232         } finally {
233             if (fos != null) {
234                 try {
235                     fos.close();
236                 } catch (IOException ignore) {}
237             }
238         }
239     }
240 
241     /** Empty current property list. Made accessible for testing purposes. */
242     @VisibleForTesting(visibility=Visibility.PRIVATE)
clear()243     protected void clear() {
244         synchronized (sSourcesProperties) {
245             sSourcesProperties.clear();
246             sModified = false;
247         }
248     }
249 }
250