/**
   * Returns the manifest file for the given module
   *
   * @param module module to search the manifest document for
   * @return manifest doc for the given module
   */
  public Manifest getManifestForModule(final Module module) {

    if (module == null || AndroidFacet.getInstance(module) == null) {
      return null;
    }

    return AndroidFacet.getInstance(module).getManifest();
  }
  /**
   * Returns the given module's apk path
   *
   * @param module android module used to get the facet and the apk file path
   * @return file path of the android apk for the given module
   */
  public String getAndroidApkPath(Module module) {

    if (module == null || AndroidFacet.getInstance(module) == null) {
      return null;
    }

    return AndroidRootUtil.getApkPath(AndroidFacet.getInstance(module));
  }
  /**
   * return the given manifest Path
   *
   * @param module
   * @return
   */
  public String getAndroidManifestPath(Module module) {

    if (module == null || AndroidFacet.getInstance(module) == null) {
      return null;
    }

    return AndroidRootUtil.getManifestFile(AndroidFacet.getInstance(module)).getPath();
  }
  @NotNull
  static String doValidatePackageName(
      boolean library, @NotNull String candidate, @Nullable ModulesProvider modulesProvider) {
    if (candidate.length() == 0) {
      return AndroidBundle.message("specify.package.name.error");
    }
    if (!AndroidUtils.isValidAndroidPackageName(candidate)) {
      return candidate;
    }
    if (!AndroidCommonUtils.contains2Identifiers(candidate)) {
      return AndroidBundle.message("package.name.must.contain.2.ids.error");
    }

    if (!library) {
      for (Module module : modulesProvider.getModules()) {
        final AndroidFacet facet = AndroidFacet.getInstance(module);
        if (facet != null && !facet.isLibraryProject()) {
          final Manifest manifest = facet.getManifest();
          if (manifest != null) {
            final String packageName = manifest.getPackage().getValue();
            if (candidate.equals(packageName)) {
              return "Package name '"
                  + packageName
                  + "' is already used by module '"
                  + module.getName()
                  + "'";
            }
          }
        }
      }
    }
    return "";
  }
 @Override
 public void apply(
     @NotNull PsiElement startElement,
     @NotNull PsiElement endElement,
     @NotNull AndroidQuickfixContexts.Context context) {
   PsiFile file = startElement.getContainingFile();
   if (file instanceof XmlFile) {
     ResourceFolderType folderType = ResourceHelper.getFolderType(file);
     if (folderType != null) {
       if (folderType != ResourceFolderType.VALUES) {
         forkResourceFile((XmlFile) file, myFolder, true);
       } else {
         XmlTag tag =
             getValueTag(PsiTreeUtil.getParentOfType(startElement, XmlTag.class, false));
         if (tag != null) {
           AndroidFacet facet = AndroidFacet.getInstance(startElement);
           if (facet != null) {
             PsiDirectory dir = null;
             if (myFolder != null) {
               PsiDirectory resFolder = findRes(file);
               if (resFolder != null) {
                 dir = resFolder.findSubdirectory(myFolder);
                 if (dir == null) {
                   dir = resFolder.createSubdirectory(myFolder);
                 }
               }
             }
             forkResourceValue(startElement.getProject(), tag, file, facet, dir);
           }
         }
       }
     }
   }
 }
 @Override
 protected boolean isAvailableForFile(PsiFile file) {
   return file instanceof XmlFile
       && AndroidFacet.getInstance(file) != null
       && DomManager.getDomManager(file.getProject()).getDomFileDescription((XmlFile) file)
           instanceof LayoutDomFileDescription;
 }
  /**
   * Create a variation (copy) of a given resource file (of a given type).
   *
   * @param xmlFile the XML resource file to fork
   * @param myNewFolder the resource folder to create, or null to ask the user
   * @param open if true, open the file after creating it
   */
  public static void forkResourceFile(
      @NotNull final XmlFile xmlFile, @Nullable String myNewFolder, boolean open) {
    VirtualFile file = xmlFile.getVirtualFile();
    if (file == null) {
      return;
    }
    Module module = AndroidPsiUtils.getModuleSafely(xmlFile);
    if (module == null) {
      return;
    }
    ResourceFolderType folderType = ResourceHelper.getFolderType(xmlFile);
    if (folderType == null || folderType == ResourceFolderType.VALUES) {
      return;
    }
    Configuration configuration = null;
    if (folderType == ResourceFolderType.LAYOUT) {
      AndroidFacet facet = AndroidFacet.getInstance(module);
      if (facet != null) {
        configuration = facet.getConfigurationManager().getConfiguration(file);
      }
    }

    // Suppress: IntelliJ claims folderType can be null here, but it can't (and inserting assert
    // folderType != null is correctly
    // identified as redundant)
    //noinspection ConstantConditions
    forkResourceFile(module, folderType, file, xmlFile, myNewFolder, configuration, open);
  }
 @Override
 protected boolean isEnabledOnElements(PsiElement[] elements) {
   if (elements.length != 1) {
     return false;
   }
   final PsiElement element = elements[0];
   return element instanceof XmlTag
       && AndroidFacet.getInstance(element) != null
       && isEnabled((XmlTag) element);
 }
  @Nullable
  public static ValidationInfo checkIfResourceAlreadyExists(
      @NotNull Module selectedModule,
      @NotNull String resourceName,
      @NotNull ResourceType resourceType,
      @NotNull List<String> dirNames,
      @NotNull String fileName) {
    if (resourceName.length() == 0 || dirNames.size() == 0 || fileName.length() == 0) {
      return null;
    }

    final AndroidFacet facet = AndroidFacet.getInstance(selectedModule);
    final VirtualFile resourceDir = facet != null ? AndroidRootUtil.getResourceDir(facet) : null;
    if (resourceDir == null) {
      return null;
    }

    for (String directoryName : dirNames) {
      final VirtualFile resourceSubdir = resourceDir.findChild(directoryName);
      if (resourceSubdir == null) {
        continue;
      }

      final VirtualFile resFile = resourceSubdir.findChild(fileName);
      if (resFile == null) {
        continue;
      }

      if (resFile.getFileType() != StdFileTypes.XML) {
        return new ValidationInfo(
            "File " + FileUtil.toSystemDependentName(resFile.getPath()) + " is not XML file");
      }

      final Resources resources =
          AndroidUtils.loadDomElement(selectedModule, resFile, Resources.class);
      if (resources == null) {
        return new ValidationInfo(
            AndroidBundle.message(
                "not.resource.file.error", FileUtil.toSystemDependentName(resFile.getPath())));
      }

      for (ResourceElement element :
          AndroidResourceUtil.getValueResourcesFromElement(resourceType.getName(), resources)) {
        if (resourceName.equals(element.getName().getValue())) {
          return new ValidationInfo(
              "resource '"
                  + resourceName
                  + "' already exists in "
                  + FileUtil.toSystemDependentName(resFile.getPath()));
        }
      }
    }
    return null;
  }
 @Override
 protected boolean isAvailableOnElementInEditorAndFile(
     PsiElement element, Editor editor, PsiFile file, DataContext context) {
   if (element == null
       || AndroidFacet.getInstance(element) == null
       || PsiTreeUtil.getParentOfType(element, XmlText.class) != null) {
     return false;
   }
   final XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class);
   return tag != null && isEnabled(tag);
 }
  public boolean render() {
    ApplicationManager.getApplication().assertIsDispatchThread();

    if (myToolWindow == null || !myToolWindow.isVisible()) {
      return false;
    }

    final PsiFile psiFile = myToolWindowForm.getFile();
    if (psiFile == null) {
      return false;
    }

    final AndroidFacet facet = AndroidFacet.getInstance(psiFile);
    if (facet == null) {
      return false;
    }

    getRenderingQueue()
        .queue(
            new Update("render") {
              @Override
              public void run() {
                ProgressManager.getInstance()
                    .runProcess(
                        new Runnable() {
                          @Override
                          public void run() {
                            DumbService.getInstance(myProject).waitForSmartMode();
                            try {
                              doRender(facet, psiFile);
                            } catch (Throwable e) {
                              LOG.error(e);
                            }
                            synchronized (PROGRESS_LOCK) {
                              if (myCurrentIndicator != null) {
                                myCurrentIndicator.stop();
                                myCurrentIndicator = null;
                              }
                            }
                          }
                        },
                        new AndroidPreviewProgressIndicator(myToolWindowForm, 100));
              }

              @Override
              public boolean canEat(Update update) {
                return true;
              }
            });
    return true;
  }
  @Override
  public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
    if (file instanceof XmlFile && file.isValid() && AndroidFacet.getInstance(file) != null) {
      ResourceFolderType folderType = ResourceHelper.getFolderType(file);
      if (folderType == null) {
        return false;
      } else if (folderType != ResourceFolderType.VALUES) {
        return true;
      } else {
        return isAvailable(getValueTag(editor, file), file);
      }
    }

    return false;
  }
  @Override
  public void invoke(@NotNull final Project project, Editor editor, PsiFile file)
      throws IncorrectOperationException {
    AndroidFacet facet = AndroidFacet.getInstance(file);
    ResourceFolderType folderType = ResourceHelper.getFolderType(file);
    if (facet == null || folderType == null) {
      // shouldn't happen; we checked in isAvailable
      return;
    }

    if (folderType != ResourceFolderType.VALUES) {
      forkResourceFile((XmlFile) file, null, true);
    } else if (editor != null) {
      forkResourceValue(project, editor, file, facet, null);
    }
  }
  private void createNewResourceFile(ResourceType resourceType) {
    AndroidFacet facet = AndroidFacet.getInstance(myModule);
    XmlFile newFile =
        CreateResourceFileAction.createFileResource(
            facet, resourceType, null, null, null, true, null);

    if (newFile != null) {
      String name = newFile.getName();
      int index = name.lastIndexOf('.');
      if (index != -1) {
        name = name.substring(0, index);
      }
      myResultResourceName = "@" + resourceType.getName() + "/" + name;
      close(OK_EXIT_CODE);
    }
  }
  public static void createSourceRootIfNotExist(
      @NotNull final String path, @NotNull final Module module) {
    ApplicationManager.getApplication().assertIsDispatchThread();

    final File rootFile = new File(path);
    final boolean created;
    if (!rootFile.exists()) {
      if (!rootFile.mkdirs()) return;
      created = true;
    } else {
      created = false;
    }

    final Project project = module.getProject();
    Module genModule = module;

    final AndroidFacet facet = AndroidFacet.getInstance(genModule);

    if (facet != null && facet.getConfiguration().LIBRARY_PROJECT) {
      removeGenModule(module);
    }

    if (project.isDisposed() || genModule.isDisposed()) {
      return;
    }

    final VirtualFile root;
    if (created) {
      root = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(rootFile);
    } else {
      root = LocalFileSystem.getInstance().findFileByIoFile(rootFile);
    }
    if (root != null) {
      final ModuleRootManager manager = ModuleRootManager.getInstance(genModule);
      unexcludeRootIfNeccessary(root, manager);
      for (VirtualFile existingRoot : manager.getSourceRoots()) {
        if (existingRoot == root) return;
      }
      ApplicationManager.getApplication()
          .runWriteAction(
              new Runnable() {
                public void run() {
                  addSourceRoot(manager, root);
                }
              });
    }
  }
  public boolean isAvailable(@Nullable XmlTag tag, PsiFile file) {
    if (file instanceof XmlFile && file.isValid() && AndroidFacet.getInstance(file) != null) {
      ResourceFolderType folderType = ResourceHelper.getFolderType(file);
      if (folderType == null) {
        return false;
      } else if (folderType != ResourceFolderType.VALUES) {
        return true;
      } else {
        // In value files, you can invoke this action if the caret is on or inside an element (other
        // than the
        // root <resources> tag). Only accept the element if it has a known type with a known name.
        if (tag != null && tag.getAttributeValue(ATTR_NAME) != null) {
          return AndroidResourceUtil.getResourceForResourceTag(tag) != null;
        }
      }
    }

    return false;
  }
  @Override
  public ProblemDescriptor[] checkFile(
      @NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
    if (!(file instanceof XmlFile)) {
      return ProblemDescriptor.EMPTY_ARRAY;
    }
    final AndroidFacet facet = AndroidFacet.getInstance(file);

    if (facet == null) {
      return ProblemDescriptor.EMPTY_ARRAY;
    }
    final DomFileDescription<?> description =
        DomManager.getDomManager(file.getProject()).getDomFileDescription((XmlFile) file);

    if (!(description instanceof LayoutDomFileDescription)
        && !(description instanceof MenuDomFileDescription)) {
      return ProblemDescriptor.EMPTY_ARRAY;
    }
    final Collection<PsiClass> activities =
        findRelatedActivities((XmlFile) file, facet, description);
    final MyVisitor visitor = new MyVisitor(manager, isOnTheFly, activities);
    file.accept(visitor);
    return visitor.myResult.toArray(new ProblemDescriptor[visitor.myResult.size()]);
  }
 private static boolean isInResourceFolder(@Nullable PsiFile psiFile) {
   if (psiFile instanceof XmlFile && AndroidFacet.getInstance(psiFile) != null) {
     return RenderService.canRender(psiFile);
   }
   return false;
 }
  private void updateDirectories(boolean updateFileCombo) {
    final Module module = getModule();
    List<VirtualFile> valuesDirs = Collections.emptyList();

    if (module != null) {
      final AndroidFacet facet = AndroidFacet.getInstance(module);

      if (facet != null) {
        myResourceDir = AndroidRootUtil.getResourceDir(facet);

        if (myResourceDir != null) {
          valuesDirs =
              AndroidResourceUtil.getResourceSubdirs(
                  ResourceFolderType.VALUES.getName(), new VirtualFile[] {myResourceDir});
        }
      }
    }

    Collections.sort(
        valuesDirs,
        new Comparator<VirtualFile>() {
          @Override
          public int compare(VirtualFile f1, VirtualFile f2) {
            return f1.getName().compareTo(f2.getName());
          }
        });

    final Map<String, JCheckBox> oldCheckBoxes = myCheckBoxes;
    final int selectedIndex = myDirectoriesList.getSelectedIndex();
    final String selectedDirName = selectedIndex >= 0 ? myDirNames[selectedIndex] : null;

    final List<JCheckBox> checkBoxList = new ArrayList<JCheckBox>();
    myCheckBoxes = new HashMap<String, JCheckBox>();
    myDirNames = new String[valuesDirs.size()];

    int newSelectedIndex = -1;

    int i = 0;

    for (VirtualFile dir : valuesDirs) {
      final String dirName = dir.getName();
      final JCheckBox oldCheckBox = oldCheckBoxes.get(dirName);
      final boolean selected = oldCheckBox != null && oldCheckBox.isSelected();
      final JCheckBox checkBox = new JCheckBox(dirName, selected);
      checkBoxList.add(checkBox);
      myCheckBoxes.put(dirName, checkBox);
      myDirNames[i] = dirName;

      if (dirName.equals(selectedDirName)) {
        newSelectedIndex = i;
      }
      i++;
    }
    myDirectoriesList.setModel(new CollectionListModel<JCheckBox>(checkBoxList));

    if (newSelectedIndex >= 0) {
      myDirectoriesList.setSelectedIndex(newSelectedIndex);
    }

    if (checkBoxList.size() == 1) {
      checkBoxList.get(0).setSelected(true);
    }

    if (updateFileCombo) {
      final Object oldItem = myFileNameCombo.getEditor().getItem();
      final Set<String> fileNameSet = new HashSet<String>();

      for (VirtualFile valuesDir : valuesDirs) {
        for (VirtualFile file : valuesDir.getChildren()) {
          fileNameSet.add(file.getName());
        }
      }
      final List<String> fileNames = new ArrayList<String>(fileNameSet);
      Collections.sort(fileNames);
      myFileNameCombo.setModel(new DefaultComboBoxModel(fileNames.toArray()));
      myFileNameCombo.getEditor().setItem(oldItem);
    }
  }
  public ChooseResourceDialog(
      @NotNull Module module,
      @NotNull ResourceType[] types,
      @Nullable String value,
      @Nullable XmlTag tag,
      ResourceNameVisibility resourceNameVisibility,
      @Nullable String colorName) {
    super(module.getProject());
    myModule = module;
    myTag = tag;
    myResourceNameVisibility = resourceNameVisibility;

    setTitle("Resources");

    AndroidFacet facet = AndroidFacet.getInstance(module);
    myProjectPanel = new ResourcePanel(facet, types, false);
    mySystemPanel = new ResourcePanel(facet, types, true);

    myContentPanel = new JBTabbedPane();
    myContentPanel.addTab("Project", myProjectPanel.myComponent);
    myContentPanel.addTab("System", mySystemPanel.myComponent);

    myProjectPanel.myTreeBuilder.expandAll(null);
    mySystemPanel.myTreeBuilder.expandAll(null);

    boolean doSelection = value != null;

    if (types == COLOR_TYPES) {
      Color color = ResourceHelper.parseColor(value);
      myColorPicker =
          new ColorPicker(
              myDisposable,
              color,
              true,
              new ColorPickerListener() {
                @Override
                public void colorChanged(Color color) {
                  notifyResourcePickerListeners(ResourceHelper.colorToString(color));
                }

                @Override
                public void closed(@Nullable Color color) {}
              });
      myColorPicker.pickARGB();

      JPanel colorPickerContent = new JPanel(new BorderLayout());
      myColorPickerPanel = new JBScrollPane(colorPickerContent);
      myColorPickerPanel.setBorder(null);
      colorPickerContent.add(myColorPicker);
      myContentPanel.addTab("Color", myColorPickerPanel);

      if (myResourceNameVisibility != ResourceNameVisibility.HIDE) {
        ResourceDialogSouthPanel resourceDialogSouthPanel = new ResourceDialogSouthPanel();
        myResourceNameField = resourceDialogSouthPanel.getResourceNameField();
        myResourceNameField.getDocument().addDocumentListener(new ValidatingDocumentListener());
        if (colorName != null) {
          myResourceNameField.setText(colorName);
        }
        myResourceNameMessage = resourceDialogSouthPanel.getResourceNameMessage();
        Color backgroundColor =
            EditorColorsManager.getInstance()
                .getGlobalScheme()
                .getColor(EditorColors.NOTIFICATION_BACKGROUND);
        myResourceNameMessage.setBackground(
            backgroundColor == null ? JBColor.YELLOW : backgroundColor);
        colorPickerContent.add(resourceDialogSouthPanel.getFullPanel(), BorderLayout.SOUTH);
        updateResourceNameStatus();
      }

      if (color != null) {
        myContentPanel.setSelectedIndex(2);
        doSelection = false;
      }
      myValidator =
          ResourceNameValidator.create(
              false,
              AppResourceRepository.getAppResources(module, true),
              ResourceType.COLOR,
              false);
    }
    if (doSelection && value.startsWith("@")) {
      value = StringUtil.replace(value, "+", "");
      int index = value.indexOf('/');
      if (index != -1) {
        ResourcePanel panel;
        String type;
        String name = value.substring(index + 1);
        if (value.startsWith(ANDROID)) {
          panel = mySystemPanel;
          type = value.substring(ANDROID.length(), index);
        } else {
          panel = myProjectPanel;
          type = value.substring(1, index);
        }
        myContentPanel.setSelectedComponent(panel.myComponent);
        panel.select(type, name);
      }
    }

    myContentPanel.addChangeListener(
        new ChangeListener() {
          @Override
          public void stateChanged(ChangeEvent e) {
            valueChanged(null);
          }
        });

    valueChanged(null);
    init();
  }