package com.intellij.lang.javascript.flex.flashbuilder; import com.intellij.CommonBundle; import com.intellij.lang.javascript.flex.FlexBundle; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.Nullable; import java.io.*; import java.net.URI; import java.net.URISyntaxException; import java.util.Enumeration; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; public class FlashBuilderProjectFinder { public static final String PROJECT_PREFS_RELATIVE_PATH = "/.metadata/.plugins/org.eclipse.core.runtime/.settings/com.adobe.flexbuilder.project.prefs"; static final String SDKS_RELATIVE_PATH = "/sdks"; private static final String PROJECTS_CACHE_RELATIVE_PATH = "/.metadata/.plugins/org.eclipse.core.resources/.projects"; private static final String DOT_LOCATION = ".location"; static boolean isFlashBuilderWorkspace(final VirtualFile file) { return file != null && file.isDirectory() && VfsUtil.findRelativeFile(PROJECT_PREFS_RELATIVE_PATH, file) != null; } static boolean isFlashBuilderWorkspace(final String dirPath) { return new File(dirPath + PROJECT_PREFS_RELATIVE_PATH).isFile(); } static boolean isFlashBuilderProject(final VirtualFile file) { final VirtualFile dir = file == null ? null : file.getParent(); return file != null && !file.isDirectory() && FlashBuilderImporter.DOT_PROJECT.equals(file.getName()) && dir != null && dir.findChild(FlashBuilderImporter.DOT_ACTION_SCRIPT_PROPERTIES) != null; } static boolean isFlashBuilderProject(final File file) { final File dir = file == null ? null : file.getParentFile(); final File dotActionScriptPropertiesFile = dir == null ? null : new File(dir, FlashBuilderImporter.DOT_ACTION_SCRIPT_PROPERTIES); return file != null && FlashBuilderImporter.DOT_PROJECT.equals(file.getName()) && file.isFile() && dotActionScriptPropertiesFile != null && dotActionScriptPropertiesFile.isFile(); } static boolean collectAllProjectPaths(final @Nullable Project project, final List<String> projectPaths, final String dirPath) { final Runnable runnable = () -> { if (isFlashBuilderWorkspace(dirPath)) { collectProjectPathsInWorkspace(projectPaths, dirPath); } else { collectProjectPathsInDirectory(projectPaths, dirPath); } }; return ProgressManager.getInstance() .runProcessWithProgressSynchronously(runnable, FlexBundle.message("looking.for.flash.builder.projects"), true, project); } private static void collectProjectPathsInWorkspace(final List<String> projectPaths, final String workspacePath) { final File projectsCacheDir = new File(workspacePath, PROJECTS_CACHE_RELATIVE_PATH); if (!projectsCacheDir.isDirectory()) return; final File[] subdirs = projectsCacheDir.listFiles(FileUtilRt.ALL_DIRECTORIES); for (File dir : subdirs) { final String dotProjectFileLocation = getDotProjectFileLocation(workspacePath, dir); if (dotProjectFileLocation != null) { projectPaths.add(dotProjectFileLocation); } } } /* the logic is taken from org.eclipse.core.internal.resources.LocalMetaArea.readPrivateDescription(IProject target, IProjectDescription description) */ @Nullable private static String getDotProjectFileLocation(final String workspacePath, final File projectCacheDir) { final String projectName = projectCacheDir.getName(); final File dotLocationFile = new File(projectCacheDir + "/" + DOT_LOCATION); if (dotLocationFile.isFile()) { DataInputStream input = null; try { input = new DataInputStream(new FileInputStream(dotLocationFile)); final int CHUNK_START_LENGTH = 16; final String URI_PREFIX = "URI//"; final long skipped = input.skip(CHUNK_START_LENGTH); if (skipped == CHUNK_START_LENGTH) { final String projectUriUri = input.readUTF(); if (projectUriUri != null && projectUriUri.startsWith(URI_PREFIX)) { final URI dotProjectUri = new URI(projectUriUri.substring(URI_PREFIX.length()) + "/" + FlashBuilderImporter.DOT_PROJECT); final File dotProjectFile = new File(dotProjectUri); if (isFlashBuilderProject(dotProjectFile)) { return dotProjectFile.getPath(); } } } } catch (IOException e) {/*ignore*/} catch (URISyntaxException e) {/*ignore*/} finally { if (input != null) { try { input.close(); } catch (IOException e) {/*ignore*/} } } } // this code is reached if no information was found in '.location' file. It means default project location. final File dotProjectFile = new File(workspacePath + "/" + projectName + "/" + FlashBuilderImporter.DOT_PROJECT); if (isFlashBuilderProject(dotProjectFile)) { return dotProjectFile.getPath(); } return null; } private static void collectProjectPathsInDirectory(final List<String> projectPaths, final String dirPath) { final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator(); if (progressIndicator != null) { progressIndicator.checkCanceled(); progressIndicator.setText2(dirPath); } final File dotProjectFile = new File(dirPath, FlashBuilderImporter.DOT_PROJECT); if (isFlashBuilderProject(dotProjectFile)) { projectPaths.add(dotProjectFile.getPath()); } else { final File root = new File(dirPath); final File[] subdirs = root.listFiles(FileUtilRt.ALL_DIRECTORIES); for (final File subdir : subdirs) { collectProjectPathsInDirectory(projectPaths, subdir.getPath()); } } } static boolean hasArchiveExtension(final String path) { return path.endsWith(FlashBuilderImporter.DOT_FXP) || path.endsWith(FlashBuilderImporter.DOT_FXPL) || path.endsWith(FlashBuilderImporter.DOT_ZIP); } static boolean hasFxpExtension(final String path) { final String lowercased = path.toLowerCase(); return lowercased.endsWith(FlashBuilderImporter.DOT_FXP) || lowercased.endsWith(FlashBuilderImporter.DOT_FXPL); } static void checkArchiveContainsFBProject(final String archiveFilePath) throws ConfigurationException { _isMultiProjectArchive(archiveFilePath); } static boolean isMultiProjectArchive(final String archiveFilePath) { try { return _isMultiProjectArchive(archiveFilePath); } catch (ConfigurationException e) { return false; } } private static boolean _isMultiProjectArchive(final String archiveFilePath) throws ConfigurationException { boolean containsDotProjectFile = false; boolean containsDotActionScriptFile = false; boolean containsNestedProjects = false; ZipFile zipFile = null; try { zipFile = new ZipFile(archiveFilePath); final Enumeration<? extends ZipEntry> entries = zipFile.entries(); while (entries.hasMoreElements()) { final String entryName = entries.nextElement().getName(); if (FlashBuilderImporter.DOT_PROJECT.equals(entryName)) containsDotProjectFile = true; if (FlashBuilderImporter.DOT_ACTION_SCRIPT_PROPERTIES.equals(entryName)) containsDotActionScriptFile = true; if (entryName.endsWith(FlashBuilderImporter.DOT_FXP) || entryName.endsWith(FlashBuilderImporter.DOT_FXPL)) { containsNestedProjects = true; } if (containsDotProjectFile && containsDotActionScriptFile && containsNestedProjects) break; } if (!containsDotProjectFile || !containsDotActionScriptFile) { throw new ConfigurationException(FlexBundle.message("does.not.contain.flash.builder.projects"), CommonBundle.getErrorTitle()); } return containsNestedProjects; } catch (IOException e) { throw new ConfigurationException(FlexBundle.message("does.not.contain.flash.builder.projects"), CommonBundle.getErrorTitle()); } finally { if (zipFile != null) { try { zipFile.close(); } catch (IOException ignore) {/*ignore*/} } } } }