1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php 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 package com.android.ide.eclipse.adt.internal.wizards.newproject; 17 18 import static com.android.SdkConstants.ATTR_NAME; 19 20 import com.android.annotations.NonNull; 21 import com.android.annotations.Nullable; 22 import com.android.ide.common.xml.AndroidManifestParser; 23 import com.android.ide.common.xml.ManifestData; 24 import com.android.ide.common.xml.ManifestData.Activity; 25 import com.android.ide.eclipse.adt.AdtPlugin; 26 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities; 27 import com.android.ide.eclipse.adt.internal.sdk.Sdk; 28 import com.android.io.FolderWrapper; 29 import com.android.sdklib.AndroidVersion; 30 import com.android.sdklib.IAndroidTarget; 31 import com.android.sdklib.internal.project.ProjectProperties; 32 import com.android.sdklib.internal.project.ProjectProperties.PropertyType; 33 import com.google.common.base.Charsets; 34 import com.google.common.io.Files; 35 36 import org.eclipse.core.resources.IResource; 37 import org.eclipse.core.resources.IWorkspace; 38 import org.eclipse.core.resources.ResourcesPlugin; 39 import org.eclipse.core.runtime.IPath; 40 import org.eclipse.core.runtime.IStatus; 41 import org.w3c.dom.Document; 42 import org.w3c.dom.Node; 43 import org.w3c.dom.NodeList; 44 import org.xml.sax.SAXException; 45 46 import java.io.File; 47 import java.io.IOException; 48 import java.util.regex.Matcher; 49 import java.util.regex.Pattern; 50 51 /** An Android project to be imported */ 52 class ImportedProject { 53 private final File mLocation; 54 private String mActivityName; 55 private ManifestData mManifest; 56 private String mProjectName; 57 private String mRelativePath; 58 59 ImportedProject(File location, String relativePath) { 60 super(); 61 mLocation = location; 62 mRelativePath = relativePath; 63 } 64 65 File getLocation() { 66 return mLocation; 67 } 68 69 String getRelativePath() { 70 return mRelativePath; 71 } 72 73 @Nullable 74 ManifestData getManifest() { 75 if (mManifest == null) { 76 try { 77 mManifest = AndroidManifestParser.parse(new FolderWrapper(mLocation)); 78 } catch (SAXException e) { 79 // Some sort of error in the manifest file: report to the user in a better way? 80 AdtPlugin.log(e, null); 81 return null; 82 } catch (Exception e) { 83 AdtPlugin.log(e, null); 84 return null; 85 } 86 } 87 88 return mManifest; 89 } 90 91 @Nullable 92 public String getActivityName() { 93 if (mActivityName == null) { 94 // Compute the project name and the package name from the manifest 95 ManifestData manifest = getManifest(); 96 if (manifest != null) { 97 if (manifest.getLauncherActivity() != null) { 98 mActivityName = manifest.getLauncherActivity().getName(); 99 } 100 if (mActivityName == null || mActivityName.isEmpty()) { 101 Activity[] activities = manifest.getActivities(); 102 for (Activity activity : activities) { 103 mActivityName = activity.getName(); 104 if (mActivityName != null && !mActivityName.isEmpty()) { 105 break; 106 } 107 } 108 } 109 if (mActivityName != null) { 110 int index = mActivityName.lastIndexOf('.'); 111 mActivityName = mActivityName.substring(index + 1); 112 } 113 } 114 } 115 116 return mActivityName; 117 } 118 119 @NonNull 120 public String getProjectName() { 121 if (mProjectName == null) { 122 // Are we importing an Eclipse project? If so just use the existing project name 123 mProjectName = findEclipseProjectName(); 124 if (mProjectName != null) { 125 return mProjectName; 126 } 127 128 String activityName = getActivityName(); 129 if (activityName == null || activityName.isEmpty()) { 130 // I could also look at the build files, say build.xml from ant, and 131 // try to glean the project name from there 132 mProjectName = mLocation.getName(); 133 } else { 134 // Try to derive it from the activity name: 135 IWorkspace workspace = ResourcesPlugin.getWorkspace(); 136 IStatus nameStatus = workspace.validateName(activityName, IResource.PROJECT); 137 if (nameStatus.isOK()) { 138 mProjectName = activityName; 139 } else { 140 // Try to derive it by escaping characters 141 StringBuilder sb = new StringBuilder(); 142 for (int i = 0, n = activityName.length(); i < n; i++) { 143 char c = activityName.charAt(i); 144 if (c != IPath.DEVICE_SEPARATOR && c != IPath.SEPARATOR && c != '\\') { 145 sb.append(c); 146 } 147 } 148 if (sb.length() == 0) { 149 mProjectName = mLocation.getName(); 150 } else { 151 mProjectName = sb.toString(); 152 } 153 } 154 } 155 } 156 157 return mProjectName; 158 } 159 160 @Nullable 161 private String findEclipseProjectName() { 162 File projectFile = new File(mLocation, ".project"); //$NON-NLS-1$ 163 if (projectFile.exists()) { 164 String xml; 165 try { 166 xml = Files.toString(projectFile, Charsets.UTF_8); 167 Document doc = DomUtilities.parseDocument(xml, false); 168 if (doc != null) { 169 NodeList names = doc.getElementsByTagName(ATTR_NAME); 170 if (names.getLength() >= 1) { 171 Node nameElement = names.item(0); 172 String name = nameElement.getTextContent().trim(); 173 if (!name.isEmpty()) { 174 return name; 175 } 176 } 177 } 178 } catch (IOException e) { 179 // pass: don't attempt to read project name; must be some sort of unrelated 180 // file with the same name, perhaps from a different editor or IDE 181 } 182 } 183 184 return null; 185 } 186 187 public void setProjectName(@NonNull String newName) { 188 mProjectName = newName; 189 } 190 191 public IAndroidTarget getTarget() { 192 // Pick a target: 193 // First try to find the one requested by project.properties 194 IAndroidTarget[] targets = Sdk.getCurrent().getTargets(); 195 ProjectProperties properties = ProjectProperties.load(mLocation.getPath(), 196 PropertyType.PROJECT); 197 if (properties != null) { 198 String targetProperty = properties.getProperty(ProjectProperties.PROPERTY_TARGET); 199 if (targetProperty != null) { 200 Matcher m = Pattern.compile("android-(.+)").matcher( //$NON-NLS-1$ 201 targetProperty.trim()); 202 if (m.matches()) { 203 String targetName = m.group(1); 204 int targetLevel; 205 try { 206 targetLevel = Integer.parseInt(targetName); 207 } catch (NumberFormatException nufe) { 208 // pass 209 targetLevel = -1; 210 } 211 for (IAndroidTarget t : targets) { 212 AndroidVersion version = t.getVersion(); 213 if (version.isPreview() && targetName.equals(version.getCodename())) { 214 return t; 215 } else if (targetLevel == version.getApiLevel()) { 216 return t; 217 } 218 } 219 if (targetLevel > 0) { 220 // If not found, pick the closest one that is higher than the 221 // api level 222 IAndroidTarget target = targets[targets.length - 1]; 223 int targetDelta = target.getVersion().getApiLevel() - targetLevel; 224 for (IAndroidTarget t : targets) { 225 int newDelta = t.getVersion().getApiLevel() - targetLevel; 226 if (newDelta >= 0 && newDelta < targetDelta) { 227 targetDelta = newDelta; 228 target = t; 229 } 230 } 231 232 return target; 233 } 234 } 235 } 236 } 237 238 // If not found, pick the closest one to the one requested by the 239 // project (in project.properties) that is still >= the minSdk version 240 IAndroidTarget target = targets[targets.length - 1]; 241 ManifestData manifest = getManifest(); 242 if (manifest != null) { 243 int minSdkLevel = manifest.getMinSdkVersion(); 244 int targetDelta = target.getVersion().getApiLevel() - minSdkLevel; 245 for (IAndroidTarget t : targets) { 246 int newDelta = t.getVersion().getApiLevel() - minSdkLevel; 247 if (newDelta >= 0 && newDelta < targetDelta) { 248 targetDelta = newDelta; 249 target = t; 250 } 251 } 252 } 253 254 return target; 255 } 256 }