• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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;
18 
19 import com.android.sdklib.SdkConstants;
20 import com.android.sdklib.SdkManager;
21 import com.android.sdklib.internal.repository.Archive.Arch;
22 import com.android.sdklib.internal.repository.Archive.Os;
23 import com.android.sdklib.repository.SdkRepository;
24 
25 import org.w3c.dom.Node;
26 
27 import java.io.File;
28 import java.util.Map;
29 import java.util.Properties;
30 
31 /**
32  * Represents a extra XML node in an SDK repository.
33  */
34 public class ExtraPackage extends MinToolsPackage
35     implements IMinApiLevelDependency {
36 
37     private static final String PROP_PATH          = "Extra.Path";         //$NON-NLS-1$
38     private static final String PROP_MIN_API_LEVEL = "Extra.MinApiLevel";  //$NON-NLS-1$
39 
40     /**
41      * The install folder name. It must be a single-segment path.
42      * The paths "add-ons", "platforms", "tools" and "docs" are reserved and cannot be used.
43      * This limitation cannot be written in the XML Schema and must be enforced here by using
44      * the method {@link #isPathValid()} *before* installing the package.
45      */
46     private final String mPath;
47 
48     /**
49      * The minimal API level required by this extra package, if > 0,
50      * or {@link #MIN_API_LEVEL_NOT_SPECIFIED} if there is no such requirement.
51      */
52     private final int mMinApiLevel;
53 
54     /**
55      * Creates a new tool package from the attributes and elements of the given XML node.
56      * <p/>
57      * This constructor should throw an exception if the package cannot be created.
58      */
ExtraPackage(RepoSource source, Node packageNode, Map<String,String> licenses)59     ExtraPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {
60         super(source, packageNode, licenses);
61 
62         mPath = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_PATH);
63 
64         mMinApiLevel = XmlParserUtils.getXmlInt(packageNode, SdkRepository.NODE_MIN_API_LEVEL,
65                 MIN_API_LEVEL_NOT_SPECIFIED);
66     }
67 
68     /**
69      * Manually create a new package with one archive and the given attributes or properties.
70      * This is used to create packages from local directories in which case there must be
71      * one archive which URL is the actual target location.
72      * <p/>
73      * By design, this creates a package with one and only one archive.
74      */
ExtraPackage(RepoSource source, Properties props, String path, int revision, String license, String description, String descUrl, Os archiveOs, Arch archiveArch, String archiveOsPath)75     ExtraPackage(RepoSource source,
76             Properties props,
77             String path,
78             int revision,
79             String license,
80             String description,
81             String descUrl,
82             Os archiveOs,
83             Arch archiveArch,
84             String archiveOsPath) {
85         super(source,
86                 props,
87                 revision,
88                 license,
89                 description,
90                 descUrl,
91                 archiveOs,
92                 archiveArch,
93                 archiveOsPath);
94 
95         // The path argument comes before whatever could be in the properties
96         mPath = path != null ? path : getProperty(props, PROP_PATH, path);
97 
98         mMinApiLevel = Integer.parseInt(
99             getProperty(props, PROP_MIN_API_LEVEL, Integer.toString(MIN_API_LEVEL_NOT_SPECIFIED)));
100     }
101 
102     /**
103      * Save the properties of the current packages in the given {@link Properties} object.
104      * These properties will later be give the constructor that takes a {@link Properties} object.
105      */
106     @Override
saveProperties(Properties props)107     void saveProperties(Properties props) {
108         super.saveProperties(props);
109 
110         props.setProperty(PROP_PATH, mPath);
111 
112         if (getMinApiLevel() != MIN_API_LEVEL_NOT_SPECIFIED) {
113             props.setProperty(PROP_MIN_API_LEVEL, Integer.toString(getMinApiLevel()));
114         }
115     }
116 
117     /**
118      * Returns the minimal API level required by this extra package, if > 0,
119      * or {@link #MIN_API_LEVEL_NOT_SPECIFIED} if there is no such requirement.
120      */
getMinApiLevel()121     public int getMinApiLevel() {
122         return mMinApiLevel;
123     }
124 
125     /**
126      * Static helper to check if a given path is acceptable for an "extra" package.
127      */
isPathValid()128     public boolean isPathValid() {
129         if (SdkConstants.FD_ADDONS.equals(mPath) ||
130                 SdkConstants.FD_PLATFORMS.equals(mPath) ||
131                 SdkConstants.FD_TOOLS.equals(mPath) ||
132                 SdkConstants.FD_DOCS.equals(mPath)) {
133             return false;
134         }
135         return mPath != null && mPath.indexOf('/') == -1 && mPath.indexOf('\\') == -1;
136     }
137 
138     /**
139      * The install folder name. It must be a single-segment path.
140      * The paths "add-ons", "platforms", "tools" and "docs" are reserved and cannot be used.
141      * This limitation cannot be written in the XML Schema and must be enforced here by using
142      * the method {@link #isPathValid()} *before* installing the package.
143      */
getPath()144     public String getPath() {
145         return mPath;
146     }
147 
148     /** Returns a short description for an {@link IDescription}. */
149     @Override
getShortDescription()150     public String getShortDescription() {
151         String name = getPath();
152         if (name != null) {
153             // Uniformize all spaces in the name and upper case words.
154 
155             name = name.replaceAll("[ _\t\f-]+", " ");     //$NON-NLS-1$ //$NON-NLS-2$
156 
157             // Look at all lower case characters in range [1..n-1] and replace them by an upper
158             // case if they are preceded by a space. Also upper cases the first character of the
159             // string.
160             boolean changed = false;
161             char[] chars = name.toCharArray();
162             for (int n = chars.length - 1, i = 0; i < n; i++) {
163                 if (Character.isLowerCase(chars[i]) && (i == 0 || chars[i - 1] == ' ')) {
164                     chars[i] = Character.toUpperCase(chars[i]);
165                     changed = true;
166                 }
167             }
168             if (changed) {
169                 name = new String(chars);
170             }
171         }
172 
173         String s = String.format("%1$s package, revision %2$d%3$s",
174                 name,
175                 getRevision(),
176                 isObsolete() ? " (Obsolete)" : "");
177 
178         return s;
179     }
180 
181     /**
182      * Returns a long description for an {@link IDescription}.
183      *
184      * The long description is whatever the XML contains for the &lt;description&gt; field,
185      * or the short description if the former is empty.
186      */
187     @Override
getLongDescription()188     public String getLongDescription() {
189         String s = getDescription();
190         if (s == null || s.length() == 0) {
191             s = String.format("Extra %1$s package", getPath());
192         }
193 
194         if (s.indexOf("revision") == -1) {
195             s += String.format("\nRevision %1$d%2$s",
196                     getRevision(),
197                     isObsolete() ? " (Obsolete)" : "");
198         }
199 
200         if (getMinToolsRevision() != MIN_TOOLS_REV_NOT_SPECIFIED) {
201             s += String.format("\nRequires tools revision %1$d", getMinToolsRevision());
202         }
203 
204         if (getMinApiLevel() != MIN_API_LEVEL_NOT_SPECIFIED) {
205             s += String.format("\nRequires SDK Platform Android API %1$s", getMinApiLevel());
206         }
207 
208         return s;
209     }
210 
211     /**
212      * Computes a potential installation folder if an archive of this package were
213      * to be installed right away in the given SDK root.
214      * <p/>
215      * A "tool" package should always be located in SDK/tools.
216      *
217      * @param osSdkRoot The OS path of the SDK root folder.
218      * @param suggestedDir A suggestion for the installation folder name, based on the root
219      *                     folder used in the zip archive.
220      * @param sdkManager An existing SDK manager to list current platforms and addons.
221      * @return A new {@link File} corresponding to the directory to use to install this package.
222      */
223     @Override
getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager)224     public File getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager) {
225         return new File(osSdkRoot, getPath());
226     }
227 
228     @Override
sameItemAs(Package pkg)229     public boolean sameItemAs(Package pkg) {
230         // Extra packages are similar if they have the same path.
231         return pkg instanceof ExtraPackage && ((ExtraPackage)pkg).mPath.equals(mPath);
232     }
233 }
234