blob: d8299b795be434b5fe02ac0b11705f29d66f6729 [file] [log] [blame]
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.android.util;
import com.android.SdkConstants;
import com.android.ddmlib.AdbCommandRejectedException;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.ShellCommandUnresponsiveException;
import com.android.ddmlib.TimeoutException;
import com.android.sdklib.internal.project.ProjectProperties;
import com.intellij.CommonBundle;
import com.intellij.codeInsight.hint.HintUtil;
import com.intellij.codeInsight.navigation.NavigationUtil;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.RunManager;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.execution.actions.ConfigurationContext;
import com.intellij.execution.configurations.ConfigurationType;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.impl.ConsoleViewImpl;
import com.intellij.execution.process.OSProcessHandler;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.facet.FacetManager;
import com.intellij.facet.ModifiableFacetModel;
import com.intellij.facet.ProjectFacetManager;
import com.intellij.ide.util.DefaultPsiElementCellRenderer;
import com.intellij.ide.wizard.CommitStepException;
import com.intellij.lang.java.JavaParserDefinition;
import com.intellij.lang.java.lexer.JavaLexer;
import com.intellij.lexer.Lexer;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationListener;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.DependencyScope;
import com.intellij.openapi.roots.ModuleOrderEntry;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.OrderEntry;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowAnchor;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
import com.intellij.openapi.wm.ex.ToolWindowManagerListener;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.search.ProjectScope;
import com.intellij.psi.tree.java.IKeywordElementType;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.content.impl.ContentImpl;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.PsiNavigateUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.DomFileElement;
import com.intellij.util.xml.DomManager;
import org.jetbrains.android.dom.manifest.Activity;
import org.jetbrains.android.dom.manifest.ActivityAlias;
import org.jetbrains.android.dom.manifest.IntentFilter;
import org.jetbrains.android.dom.manifest.Manifest;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.AndroidFacetConfiguration;
import org.jetbrains.android.run.*;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.List;
/**
* @author yole, coyote
*/
public class AndroidUtils {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.util.AndroidUtils");
@NonNls public static final String NAMESPACE_KEY = "android";
@NonNls public static final String SYSTEM_RESOURCE_PACKAGE = "android";
// Classes and constants
@NonNls public static final String VIEW_CLASS_NAME = SdkConstants.CLASS_VIEW;
@NonNls public static final String APPLICATION_CLASS_NAME = SdkConstants.CLASS_APPLICATION;
@NonNls public static final String ACTIVITY_BASE_CLASS_NAME = SdkConstants.CLASS_ACTIVITY;
@NonNls public static final String R_CLASS_NAME = SdkConstants.R_CLASS;
@NonNls public static final String MANIFEST_CLASS_NAME = SdkConstants.FN_MANIFEST_BASE;
@NonNls public static final String LAUNCH_ACTION_NAME = "android.intent.action.MAIN";
@NonNls public static final String WALLPAPER_SERVICE_ACTION_NAME = "android.service.wallpaper.WallpaperService";
@NonNls public static final String LAUNCH_CATEGORY_NAME = "android.intent.category.LAUNCHER";
@NonNls public static final String LEANBACK_LAUNCH_CATEGORY_NAME = "android.intent.category.LEANBACK_LAUNCHER";
@NonNls public static final String DEFAULT_CATEGORY_NAME = "android.intent.category.DEFAULT";
@NonNls public static final String WATCHFACE_CATEGORY_NAME = "com.google.android.wearable.watchface.category.WATCH_FACE";
@NonNls public static final String INSTRUMENTATION_RUNNER_BASE_CLASS = SdkConstants.CLASS_INSTRUMENTATION;
@NonNls public static final String SERVICE_CLASS_NAME = SdkConstants.CLASS_SERVICE;
@NonNls public static final String RECEIVER_CLASS_NAME = SdkConstants.CLASS_BROADCASTRECEIVER;
@NonNls public static final String PROVIDER_CLASS_NAME = SdkConstants.CLASS_CONTENTPROVIDER;
public static final int TIMEOUT = 3000000;
private static final Key<ConsoleView> CONSOLE_VIEW_KEY = new Key<ConsoleView>("AndroidConsoleView");
// Properties
@NonNls public static final String ANDROID_LIBRARY_PROPERTY = SdkConstants.ANDROID_LIBRARY;
@NonNls public static final String ANDROID_MANIFEST_MERGER_PROPERTY = "manifestmerger.enabled";
@NonNls public static final String ANDROID_DEX_DISABLE_MERGER = "dex.disable.merger";
@NonNls public static final String ANDROID_DEX_FORCE_JUMBO_PROPERTY = "dex.force.jumbo";
@NonNls public static final String ANDROID_TARGET_PROPERTY = ProjectProperties.PROPERTY_TARGET;
@NonNls public static final String ANDROID_LIBRARY_REFERENCE_PROPERTY_PREFIX = "android.library.reference.";
@NonNls public static final String TAG_LINEAR_LAYOUT = SdkConstants.LINEAR_LAYOUT;
private static final String[] ANDROID_COMPONENT_CLASSES = new String[]{ACTIVITY_BASE_CLASS_NAME,
SERVICE_CLASS_NAME, RECEIVER_CLASS_NAME, PROVIDER_CLASS_NAME};
private AndroidUtils() {
}
@Nullable
public static <T extends DomElement> T loadDomElement(@NotNull final Module module,
@NotNull final VirtualFile file,
@NotNull final Class<T> aClass) {
return loadDomElement(module.getProject(), file, aClass);
}
@Nullable
public static <T extends DomElement> T loadDomElement(@NotNull final Project project,
@NotNull final VirtualFile file,
@NotNull final Class<T> aClass) {
return ApplicationManager.getApplication().runReadAction(new Computable<T>() {
@Override
@Nullable
public T compute() {
if (project.isDisposed()) return null;
PsiFile psiFile = PsiManager.getInstance(project).findFile(file);
if (psiFile instanceof XmlFile) {
return loadDomElementWithReadPermission(project, (XmlFile)psiFile, aClass);
}
else {
return null;
}
}
});
}
/** This method should be called under a read action. */
@Nullable
public static <T extends DomElement> T loadDomElementWithReadPermission(@NotNull Project project,
@NotNull XmlFile xmlFile,
@NotNull Class<T> aClass) {
ApplicationManager.getApplication().assertReadAccessAllowed();
DomManager domManager = DomManager.getDomManager(project);
DomFileElement<T> element = domManager.getFileElement(xmlFile, aClass);
if (element == null) return null;
return element.getRootElement();
}
@Nullable
public static VirtualFile findSourceRoot(@NotNull Module module, VirtualFile file) {
final Set<VirtualFile> sourceRoots = new HashSet<VirtualFile>();
Collections.addAll(sourceRoots, ModuleRootManager.getInstance(module).getSourceRoots());
while (file != null) {
if (sourceRoots.contains(file)) {
return file;
}
file = file.getParent();
}
return null;
}
@Nullable
public static String computePackageName(@NotNull Module module, VirtualFile file) {
final Set<VirtualFile> sourceRoots = new HashSet<VirtualFile>();
Collections.addAll(sourceRoots, ModuleRootManager.getInstance(module).getSourceRoots());
final VirtualFile projectDir = module.getProject().getBaseDir();
final List<String> packages = new ArrayList<String>();
file = file.getParent();
while (file != null && !Comparing.equal(projectDir, file) && !sourceRoots.contains(file)) {
packages.add(file.getName());
file = file.getParent();
}
if (file != null && sourceRoots.contains(file)) {
final StringBuilder packageName = new StringBuilder();
for (int i = packages.size() - 1; i >= 0; i--) {
packageName.append(packages.get(i));
if (i > 0) packageName.append('.');
}
return packageName.toString();
}
return null;
}
public static void addRunConfiguration(@NotNull final AndroidFacet facet, @Nullable final String activityClass, final boolean ask,
@Nullable final TargetSelectionMode targetSelectionMode,
@Nullable final String preferredAvdName) {
final Module module = facet.getModule();
final Project project = module.getProject();
final Runnable r = new Runnable() {
@Override
public void run() {
final RunManager runManager = RunManager.getInstance(project);
final RunnerAndConfigurationSettings settings = runManager.
createRunConfiguration(module.getName(), AndroidRunConfigurationType.getInstance().getFactory());
final AndroidRunConfiguration configuration = (AndroidRunConfiguration)settings.getConfiguration();
configuration.setModule(module);
if (activityClass != null) {
configuration.MODE = AndroidRunConfiguration.LAUNCH_SPECIFIC_ACTIVITY;
configuration.ACTIVITY_CLASS = activityClass;
}
else if (AndroidRunConfiguration.isWatchFaceApp(facet)) {
// In case of a watch face app, there is only a service and no default activity that can be launched
// Eventually, we'd need to support launching a service, but currently you cannot launch a watch face service as well.
// See https://code.google.com/p/android/issues/detail?id=151353
configuration.MODE = AndroidRunConfiguration.DO_NOTHING;
}
else {
configuration.MODE = AndroidRunConfiguration.LAUNCH_DEFAULT_ACTIVITY;
}
if (targetSelectionMode != null) {
configuration.setTargetSelectionMode(targetSelectionMode);
}
if (preferredAvdName != null) {
configuration.PREFERRED_AVD = preferredAvdName;
}
runManager.addConfiguration(settings, false);
runManager.setSelectedConfiguration(settings);
}
};
if (!ask) {
r.run();
}
else {
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
final String moduleName = facet.getModule().getName();
final int result = Messages.showYesNoDialog(project, AndroidBundle.message("create.run.configuration.question", moduleName),
AndroidBundle.message("create.run.configuration.title"), Messages.getQuestionIcon());
if (result == Messages.YES) {
r.run();
}
}
});
}
}
public static boolean isAbstract(@NotNull PsiClass c) {
return (c.isInterface() || c.hasModifierProperty(PsiModifier.ABSTRACT));
}
public static void executeCommandOnDevice(@NotNull IDevice device,
@NotNull String command,
@NotNull AndroidOutputReceiver receiver,
boolean infinite)
throws IOException, TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
int attempt = 0;
while (attempt < 5) {
if (infinite) {
device.executeShellCommand(command, receiver, 0);
}
else {
device.executeShellCommand(command, receiver, TIMEOUT);
}
if (infinite && !receiver.isCancelled()) {
attempt++;
}
else if (receiver.isTryAgain()) {
attempt++;
}
else {
break;
}
receiver.invalidate();
}
}
@Nullable
public static Module getAndroidModule(ConfigurationContext context) {
Module module = context.getModule();
if (module == null || AndroidFacet.getInstance(module) == null) {
return null;
}
return module;
}
public static VirtualFile createChildDirectoryIfNotExist(Project project, VirtualFile parent, String name) throws IOException {
final VirtualFile child = parent.findChild(name);
return child == null ? parent.createChildDirectory(project, name) : child;
}
@Nullable
public static PsiFile getContainingFile(@NotNull PsiElement element) {
return element instanceof PsiFile ? (PsiFile)element : element.getContainingFile();
}
public static void navigateTo(@NotNull PsiElement[] targets, @Nullable RelativePoint pointToShowPopup) {
if (targets.length == 0) {
final JComponent renderer = HintUtil.createErrorLabel("Empty text");
final JBPopup popup = JBPopupFactory.getInstance().createComponentPopupBuilder(renderer, renderer).createPopup();
if (pointToShowPopup != null) {
popup.show(pointToShowPopup);
}
return;
}
if (targets.length == 1 || pointToShowPopup == null) {
PsiNavigateUtil.navigate(targets[0]);
}
else {
DefaultPsiElementCellRenderer renderer = new DefaultPsiElementCellRenderer() {
@Override
public String getElementText(PsiElement element) {
final PsiFile file = getContainingFile(element);
return file != null ? file.getName() : super.getElementText(element);
}
@Override
public String getContainerText(PsiElement element, String name) {
final PsiFile file = getContainingFile(element);
final PsiDirectory dir = file != null ? file.getContainingDirectory() : null;
return dir == null ? "" : '(' + dir.getName() + ')';
}
};
final JBPopup popup = NavigationUtil.getPsiElementPopup(targets, renderer, null);
popup.show(pointToShowPopup);
}
}
@NotNull
public static ExecutionStatus executeCommand(@NotNull GeneralCommandLine commandLine,
@Nullable final OutputProcessor processor,
@Nullable WaitingStrategies.Strategy strategy) throws ExecutionException {
LOG.info(commandLine.getCommandLineString());
OSProcessHandler handler = new OSProcessHandler(commandLine.createProcess(), "");
final ProcessAdapter listener = new ProcessAdapter() {
@Override
public void onTextAvailable(final ProcessEvent event, final Key outputType) {
if (processor != null) {
final String message = event.getText();
processor.onTextAvailable(message);
}
}
};
if (!(strategy instanceof WaitingStrategies.DoNotWait)) {
handler.addProcessListener(listener);
}
handler.startNotify();
try {
if (!(strategy instanceof WaitingStrategies.WaitForever)) {
if (strategy instanceof WaitingStrategies.WaitForTime) {
handler.waitFor(((WaitingStrategies.WaitForTime)strategy).getTimeMs());
}
}
else {
handler.waitFor();
}
}
catch (ProcessCanceledException e) {
return ExecutionStatus.ERROR;
}
if (!handler.isProcessTerminated()) {
return ExecutionStatus.TIMEOUT;
}
if (!(strategy instanceof WaitingStrategies.DoNotWait)) {
handler.removeProcessListener(listener);
}
int exitCode = handler.getProcess().exitValue();
return exitCode == 0 ? ExecutionStatus.SUCCESS : ExecutionStatus.ERROR;
}
@NotNull
public static String getSimpleNameByRelativePath(@NotNull String relativePath) {
relativePath = FileUtil.toSystemIndependentName(relativePath);
int index = relativePath.lastIndexOf('/');
if (index < 0) {
return relativePath;
}
return relativePath.substring(index + 1);
}
public static void printMessageToConsole(@NotNull Project project, @NotNull String s, @NotNull ConsoleViewContentType contentType) {
final ConsoleView consoleView = project.getUserData(CONSOLE_VIEW_KEY);
if (consoleView != null) {
consoleView.print(s + '\n', contentType);
}
}
public static void activateConsoleToolWindow(@NotNull Project project, @NotNull final Runnable runAfterActivation) {
final ToolWindowManager manager = ToolWindowManager.getInstance(project);
final String toolWindowId = AndroidBundle.message("android.console.tool.window.title");
ToolWindow toolWindow = manager.getToolWindow(toolWindowId);
if (toolWindow != null) {
runAfterActivation.run();
return;
}
toolWindow = manager.registerToolWindow(toolWindowId, true, ToolWindowAnchor.BOTTOM);
final ConsoleView console = new ConsoleViewImpl(project, false);
project.putUserData(CONSOLE_VIEW_KEY, console);
toolWindow.getContentManager().addContent(new ContentImpl(console.getComponent(), "", false));
final ToolWindowManagerListener listener = new ToolWindowManagerListener() {
@Override
public void toolWindowRegistered(@NotNull String id) {
}
@Override
public void stateChanged() {
ToolWindow window = manager.getToolWindow(toolWindowId);
if (window != null && !window.isVisible()) {
((ToolWindowManagerEx)manager).removeToolWindowManagerListener(this);
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
manager.unregisterToolWindow(toolWindowId);
}
});
}
}
};
toolWindow.show(new Runnable() {
@Override
public void run() {
runAfterActivation.run();
((ToolWindowManagerEx)manager).addToolWindowManagerListener(listener);
}
});
}
@NotNull
public static AndroidFacet addAndroidFacetInWriteAction(@NotNull final Module module,
@NotNull final VirtualFile contentRoot,
final boolean library) {
return ApplicationManager.getApplication().runWriteAction(new Computable<AndroidFacet>() {
@Override
public AndroidFacet compute() {
return addAndroidFacet(module, contentRoot, library);
}
});
}
@NotNull
public static AndroidFacet addAndroidFacet(final Module module, @NotNull VirtualFile contentRoot,
boolean library) {
final FacetManager facetManager = FacetManager.getInstance(module);
ModifiableFacetModel model = facetManager.createModifiableModel();
AndroidFacet facet = model.getFacetByType(AndroidFacet.ID);
if (facet == null) {
facet = facetManager.createFacet(AndroidFacet.getFacetType(), "Android", null);
AndroidFacetConfiguration configuration = facet.getConfiguration();
configuration.init(module, contentRoot);
if (library) {
configuration.getState().LIBRARY_PROJECT = true;
}
model.addFacet(facet);
}
model.commit();
return facet;
}
@Nullable
public static VirtualFile findFileByAbsoluteOrRelativePath(@Nullable VirtualFile baseDir, @NotNull String path) {
VirtualFile libDir = LocalFileSystem.getInstance().findFileByPath(path);
if (libDir != null) {
return libDir;
}
else if (baseDir != null) {
return LocalFileSystem.getInstance().findFileByPath(baseDir.getPath() + '/' + path);
}
return null;
}
public static int getIntAttrValue(@NotNull final XmlTag tag, @NotNull final String attrName) {
String value = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
@Override
public String compute() {
return tag.getAttributeValue(attrName, SdkConstants.NS_RESOURCES);
}
});
try {
return Integer.parseInt(value);
}
catch (NumberFormatException e) {
return -1;
}
}
public static void collectFiles(@NotNull VirtualFile root, @NotNull Set<VirtualFile> visited, @NotNull Set<VirtualFile> result) {
if (!visited.add(root)) {
return;
}
if (root.isDirectory()) {
for (VirtualFile child : root.getChildren()) {
collectFiles(child, visited, result);
}
}
else {
result.add(root);
}
}
@Nullable
public static TargetSelectionMode getDefaultTargetSelectionMode(@NotNull Module module,
@NotNull ConfigurationType type,
@NonNls ConfigurationType alternativeType) {
final RunManager runManager = RunManager.getInstance(module.getProject());
List<RunConfiguration> configurations = runManager.getConfigurationsList(type);
TargetSelectionMode alternative = null;
if (configurations.size() > 0) {
for (RunConfiguration configuration : configurations) {
if (configuration instanceof AndroidRunConfigurationBase) {
final AndroidRunConfigurationBase runConfig = (AndroidRunConfigurationBase)configuration;
final TargetSelectionMode targetMode = runConfig.getTargetSelectionMode();
if (runConfig.getConfigurationModule() == module) {
return targetMode;
}
else {
alternative = targetMode;
}
}
}
}
if (alternative != null) {
return alternative;
}
configurations = runManager.getConfigurationsList(alternativeType);
if (configurations.size() > 0) {
for (RunConfiguration configuration : configurations) {
if (configuration instanceof AndroidRunConfigurationBase) {
return ((AndroidRunConfigurationBase)configuration).getTargetSelectionMode();
}
}
}
return null;
}
public static boolean equal(@Nullable String s1, @Nullable String s2, boolean distinguishDelimeters) {
if (s1 == null || s2 == null) {
return false;
}
if (s1.length() != s2.length()) return false;
for (int i = 0, n = s1.length(); i < n; i++) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
if (distinguishDelimeters || (Character.isLetterOrDigit(c1) && Character.isLetterOrDigit(c2))) {
if (c1 != c2) return false;
}
}
return true;
}
@NotNull
public static List<AndroidFacet> getApplicationFacets(@NotNull Project project) {
final List<AndroidFacet> result = new ArrayList<AndroidFacet>();
for (AndroidFacet facet : ProjectFacetManager.getInstance(project).getFacets(AndroidFacet.ID)) {
if (!facet.isLibraryProject()) {
result.add(facet);
}
}
return result;
}
@NotNull
public static List<AndroidFacet> getAndroidLibraryDependencies(@NotNull Module module) {
final List<AndroidFacet> depFacets = new ArrayList<AndroidFacet>();
for (OrderEntry orderEntry : ModuleRootManager.getInstance(module).getOrderEntries()) {
if (orderEntry instanceof ModuleOrderEntry) {
final ModuleOrderEntry moduleOrderEntry = (ModuleOrderEntry)orderEntry;
if (moduleOrderEntry.getScope() == DependencyScope.COMPILE) {
final Module depModule = moduleOrderEntry.getModule();
if (depModule != null) {
final AndroidFacet depFacet = AndroidFacet.getInstance(depModule);
if (depFacet != null && depFacet.isLibraryProject()) {
depFacets.add(depFacet);
}
}
}
}
}
return depFacets;
}
@NotNull
public static List<AndroidFacet> getAllAndroidDependencies(@NotNull Module module, boolean androidLibrariesOnly) {
return AndroidDependenciesCache.getInstance(module).getAllAndroidDependencies(androidLibrariesOnly);
}
@NotNull
public static Set<String> getDepLibsPackages(Module module) {
final Set<String> result = new HashSet<String>();
final HashSet<Module> visited = new HashSet<Module>();
if (visited.add(module)) {
for (AndroidFacet depFacet : getAllAndroidDependencies(module, true)) {
final Manifest manifest = depFacet.getManifest();
if (manifest != null) {
String aPackage = manifest.getPackage().getValue();
if (aPackage != null) {
result.add(aPackage);
}
}
}
}
return result;
}
public static void checkNewPassword(JPasswordField passwordField, JPasswordField confirmedPasswordField) throws CommitStepException {
char[] password = passwordField.getPassword();
char[] confirmedPassword = confirmedPasswordField.getPassword();
try {
checkPassword(password);
if (password.length < 6) {
throw new CommitStepException(AndroidBundle.message("android.export.package.incorrect.password.length"));
}
if (!Arrays.equals(password, confirmedPassword)) {
throw new CommitStepException(AndroidBundle.message("android.export.package.passwords.not.match.error"));
}
}
finally {
Arrays.fill(password, '\0');
Arrays.fill(confirmedPassword, '\0');
}
}
public static void checkPassword(char[] password) throws CommitStepException {
if (password.length == 0) {
throw new CommitStepException(AndroidBundle.message("android.export.package.specify.password.error"));
}
}
public static void checkPassword(JPasswordField passwordField) throws CommitStepException {
char[] password = passwordField.getPassword();
try {
checkPassword(password);
}
finally {
Arrays.fill(password, '\0');
}
}
@NotNull
public static <T> List<T> toList(@NotNull Enumeration<T> enumeration) {
return ContainerUtil.toList(enumeration);
}
public static void reportError(@NotNull Project project, @NotNull String message) {
reportError(project, message, CommonBundle.getErrorTitle());
}
public static void reportError(@NotNull Project project, @NotNull String message, @NotNull String title) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
throw new IncorrectOperationException(message);
}
else {
Messages.showErrorDialog(project, message, title);
}
}
public static void showStackStace(@NotNull final Project project, @NotNull Throwable[] throwables) {
final StringBuilder messageBuilder = new StringBuilder();
for (Throwable t : throwables) {
if (messageBuilder.length() > 0) {
messageBuilder.append("\n\n");
}
messageBuilder.append(AndroidCommonUtils.getStackTrace(t));
}
final DialogWrapper wrapper = new DialogWrapper(project, false) {
{
init();
}
@Override
protected JComponent createCenterPanel() {
final JPanel panel = new JPanel(new BorderLayout());
final JTextArea textArea = new JTextArea(messageBuilder.toString());
textArea.setEditable(false);
textArea.setRows(40);
textArea.setColumns(70);
panel.add(ScrollPaneFactory.createScrollPane(textArea));
return panel;
}
};
wrapper.setTitle("Stack trace");
wrapper.show();
}
/**
* Checks if the given name is a valid Android application package (which has
* additional requirements beyond a normal Java package)
*
* @see #validateAndroidPackageName(String)
*/
public static boolean isValidAndroidPackageName(@NotNull String name) {
return validateAndroidPackageName(name) == null;
}
/**
* Checks if the given name is a valid general Java package name.
* <p>
* If validating the Android package name, use {@link #validateAndroidPackageName(String)} instead!
*/
public static boolean isValidJavaPackageName(@NotNull String name) {
int index = 0;
while (true) {
int index1 = name.indexOf('.', index);
if (index1 < 0) index1 = name.length();
if (!isIdentifier(name.substring(index, index1))) return false;
if (index1 == name.length()) return true;
index = index1 + 1;
}
}
/**
* Validates a potential package name and returns null if the package name is valid, and otherwise
* returns a description for why it is not valid.
* <p>
* Note that Android package names are more restrictive than general Java package names;
* we require at least two segments, limit the character set to [a-zA-Z0-9_] (Java allows any
* {@link Character#isLetter(char)} and require that each segment start with a letter (Java allows
* underscores at the beginning).
* <p>
* For details, see core/java/android/content/pm/PackageParser.java#validateName
*
* @param name the package name
* @return null if the package is valid as an Android package name, and otherwise a description for why not
*/
@Nullable
public static String validateAndroidPackageName(@NotNull String name) {
if (name.isEmpty()) {
return "Package name is missing";
}
String packageManagerCheck = validateName(name, true);
if (packageManagerCheck != null) {
return packageManagerCheck;
}
// In addition, we have to check that none of the segments are Java identifiers, since
// that will lead to compilation errors, which the package manager doesn't need to worry about
// (the code wouldn't have compiled)
ApplicationManager.getApplication().assertReadAccessAllowed();
Lexer lexer = JavaParserDefinition.createLexer(LanguageLevel.JDK_1_5);
int index = 0;
while (true) {
int index1 = name.indexOf('.', index);
if (index1 < 0) {
index1 = name.length();
}
String segment = name.substring(index, index1);
lexer.start(segment);
if (lexer.getTokenType() != JavaTokenType.IDENTIFIER) {
if (lexer.getTokenType() instanceof IKeywordElementType) {
return "Package names cannot contain Java keywords like '" + segment + "'";
}
if (segment.isEmpty()) {
return "Package segments must be of non-zero length";
}
return segment + " is not a valid identifier";
}
if (index1 == name.length()) {
break;
}
index = index1 + 1;
}
return null;
}
// This method is a copy of android.content.pm.PackageParser#validateName with the
// error messages tweaked
@Nullable
private static String validateName(String name, boolean requiresSeparator) {
final int N = name.length();
boolean hasSep = false;
boolean front = true;
for (int i=0; i<N; i++) {
final char c = name.charAt(i);
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
front = false;
continue;
}
if ((c >= '0' && c <= '9') || c == '_') {
if (!front) {
continue;
} else {
if (c == '_') {
return "The character '_' cannot be the first character in a package segment";
} else {
return "A digit cannot be the first character in a package segment";
}
}
}
if (c == '.') {
hasSep = true;
front = true;
continue;
}
return "The character '" + c + "' is not allowed in Android application package names";
}
return hasSep || !requiresSeparator ? null : "The package must have at least one '.' separator";
}
public static boolean isIdentifier(@NotNull String candidate) {
return StringUtil.isJavaIdentifier(candidate) && !JavaLexer.isKeyword(candidate, LanguageLevel.JDK_1_5);
}
public static void reportImportErrorToEventLog(String message, String modName, Project project) {
reportImportErrorToEventLog(message, modName, project, null);
}
public static void reportImportErrorToEventLog(String message, String modName, Project project, NotificationListener listener) {
Notifications.Bus.notify(new Notification(AndroidBundle.message("android.facet.importing.notification.group"),
AndroidBundle.message("android.facet.importing.title", modName),
message, NotificationType.ERROR, listener), project);
LOG.debug(message);
}
public static boolean isPackagePrefix(@NotNull String prefix, @NotNull String name) {
return name.equals(prefix) || name.startsWith(prefix + ".");
}
@NotNull
public static Set<Module> getSetWithBackwardDependencies(@NotNull Collection<Module> modules) {
final Set<Module> set = new HashSet<Module>();
for (Module module : modules) {
collectModules(module, set, ModuleManager.getInstance(module.getProject()).getModules());
}
return set;
}
private static void collectModules(Module module, Set<Module> result, Module[] allModules) {
if (!result.add(module)) {
return;
}
for (Module otherModule : allModules) {
if (ModuleRootManager.getInstance(otherModule).isDependsOn(module)) {
collectModules(otherModule, result, allModules);
}
}
}
@NotNull
public static List<String> urlsToOsPaths(@NotNull List<String> urls, @Nullable String sdkHomeCanonicalPath) {
if (urls.isEmpty()) {
return Collections.emptyList();
}
final List<String> result = new ArrayList<String>(urls.size());
for (String url : urls) {
if (sdkHomeCanonicalPath != null) {
url = StringUtil.replace(url, AndroidCommonUtils.SDK_HOME_MACRO, sdkHomeCanonicalPath);
}
result.add(FileUtil.toSystemDependentName(VfsUtilCore.urlToPath(url)));
}
return result;
}
@NotNull
public static String getAndroidSystemDirectoryOsPath() {
return PathManager.getSystemPath() + File.separator + "android";
}
public static boolean isAndroidComponent(@NotNull PsiClass c) {
final Project project = c.getProject();
final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
for (String componentClassName : ANDROID_COMPONENT_CLASSES) {
final PsiClass componentClass = facade.findClass(componentClassName, ProjectScope.getAllScope(project));
if (componentClass != null && c.isInheritor(componentClass, true)) {
return true;
}
}
return false;
}
}