package com.intellij.flex.uiDesigner.libraries;
import com.intellij.flex.model.bc.TargetPlatform;
import com.intellij.flex.uiDesigner.LogMessageUtil;
import com.intellij.javascript.flex.resolve.ActionScriptClassResolver;
import com.intellij.lang.javascript.flex.projectStructure.model.*;
import com.intellij.lang.javascript.flex.projectStructure.options.BCUtils;
import com.intellij.lang.javascript.flex.projectStructure.options.FlexProjectRootsUtil;
import com.intellij.lang.javascript.flex.sdk.FlexmojosSdkType;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkType;
import com.intellij.openapi.roots.LibraryOrderEntry;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.psi.search.GlobalSearchScope;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
class LibraryCollector {
private static final String DOT_SWC = ".swc";
final List<Library> externalLibraries = new ArrayList<>();
final List<Library> sdkLibraries = new ArrayList<>();
private VirtualFile globalLibrary;
final LibraryStyleInfoCollector initializer;
private final Module module;
private String flexmojosSdkHomePath;
// AS-200
private final Set<VirtualFile> uniqueGuard = new THashSet<>();
private final LibraryManager libraryManager;
private String flexSdkVersion;
public LibraryCollector(LibraryManager libraryManager, LibraryStyleInfoCollector initializer, Module module) {
this.libraryManager = libraryManager;
this.initializer = initializer;
this.module = module;
}
public String getFlexSdkVersion() {
return flexSdkVersion;
}
@NotNull
public VirtualFile getGlobalLibrary() {
return globalLibrary;
}
@SuppressWarnings("SpellCheckingInspection")
private static boolean isAutomationOrUselessLibrary(String name) {
return name.startsWith("qtp") || name.startsWith("automation")
|| name.equals("flex.swc") /* flex.swc is only aggregation library */
|| name.equals("servicemonitor.swc") /* aircore contains all classes */
|| name.equals("utilities.swc") /* flex sdk 4.1 */
|| name.equals("core.swc") /* hero (4.5) aggregation library */
|| name.equals("applicationupdater.swc") /* applicationupdater_ui contains all classes */
|| name.equals("flash-integration.swc") || name.equals("authoringsupport.swc");
}
private boolean isGlobalLibrary(String name, VirtualFile jarFile, String prefix) {
if (flexmojosSdkHomePath == null) {
return name.equals(prefix + DOT_SWC);
}
else {
return jarFile.getPath().startsWith(flexmojosSdkHomePath) && name.startsWith(prefix);
}
}
private boolean isGlobalLibrary(String name, VirtualFile jarFile) {
final boolean isAirglobal = isGlobalLibrary(name, jarFile, "airglobal");
final boolean isGlobal = isAirglobal || isGlobalLibrary(name, jarFile, "playerglobal");
// flexmojos project may has playerglobal and airglobal simultaneous
if (isGlobal && (globalLibrary == null || isAirglobal)) {
globalLibrary = Library.getCatalogFile(jarFile);
}
return isGlobal;
}
@Nullable
private VirtualFile getRealFileIfValidSwc(final VirtualFile jarFile) {
if (jarFile.getFileSystem() instanceof JarFileSystem) {
VirtualFile file = JarFileSystem.getInstance().getVirtualFileForJar(jarFile);
if (file != null && !file.isDirectory() && file.getName().endsWith(DOT_SWC) && !isGlobalLibrary(file.getName(), jarFile) &&
isSwfAndCatalogExists(jarFile) && uniqueGuard.add(file)) {
return file;
}
}
return null;
}
private void collectSdkLibraries(final FlexBuildConfiguration bc, Sdk sdk) {
for (VirtualFile jarFile : sdk.getRootProvider().getFiles(OrderRootType.CLASSES)) {
String swcPath = VirtualFileManager.extractPath(StringUtil.trimEnd(jarFile.getUrl(), JarFileSystem.JAR_SEPARATOR));
if (BCUtils.getSdkEntryLinkageType(swcPath, bc) != null) {
VirtualFile file = getRealFileIfValidSwc(jarFile);
if (file != null && !isAutomationOrUselessLibrary(file.getName())) {
addLibrary(jarFile, true);
}
}
}
}
/**
* We don't use BuildConfigurationEntry as source of libraries. If reference to component declared in such build configuration is resolved, so, we register such bc's module
*/
public void collect(Module module) {
final FlexBuildConfiguration bc = FlexBuildConfigurationManager.getInstance(module).getActiveConfiguration();
final Sdk sdk = bc.getSdk();
assert sdk != null;
final SdkType sdkType;
try {
sdkType = (SdkType)sdk.getClass().getMethod("getSdkType").invoke(sdk);
}
catch (Exception e) {
throw new RuntimeException(e);
}
final boolean isFlexmojosSdk = sdkType instanceof FlexmojosSdkType;
if (!isFlexmojosSdk) {
collectSdkLibraries(bc, sdk);
}
else {
final String sdkHomePath = sdk.getHomePath();
LogMessageUtil.LOG.assertTrue(sdkHomePath != null && sdkHomePath.contains("flex"), sdkHomePath + " must be path to maven repo and contains 'flex'");
flexmojosSdkHomePath = sdkHomePath.substring(0, sdkHomePath.indexOf("flex"));
}
flexSdkVersion = sdk.getVersionString();
assert flexSdkVersion != null;
if (StringUtil.compareVersionNumbers(flexSdkVersion, "4.5.1") >= 0) {
flexSdkVersion = "4.6";
}
else {
flexSdkVersion = flexSdkVersion.substring(0, 3);
}
globalCatalogForTests(bc);
final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
for (DependencyEntry entry : bc.getDependencies().getEntries()) {
if (entry instanceof ModuleLibraryEntry) {
LibraryOrderEntry orderEntry = FlexProjectRootsUtil.findOrderEntry((ModuleLibraryEntry)entry, moduleRootManager);
if (orderEntry != null) {
collectFromLibraryOrderEntry(orderEntry.getRootFiles(OrderRootType.CLASSES));
}
}
else if (entry instanceof SharedLibraryEntry) {
com.intellij.openapi.roots.libraries.Library library = FlexProjectRootsUtil.findOrderEntry(module.getProject(),
(SharedLibraryEntry)entry);
if (library != null) {
collectFromLibraryOrderEntry(library.getFiles(OrderRootType.CLASSES));
}
}
}
// IDEA-71055
// todo css-based themes
final List<String> themes = BCUtils.getThemes(module, bc);
for (String theme : themes) {
if (theme.endsWith(DOT_SWC)) {
final VirtualFile file = LocalFileSystem.getInstance().findFileByPath(theme);
if (file != null && uniqueGuard.add(file)) {
final VirtualFile jarFile = JarFileSystem.getInstance().getJarRootForLocalFile(file);
if (jarFile != null) {
addLibrary(jarFile, true);
}
}
}
}
}
private void globalCatalogForTests(FlexBuildConfiguration bc) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
assert globalLibrary == null;
//noinspection TestOnlyProblems
globalLibrary = LibraryUtil.getTestGlobalLibrary(bc.getTargetPlatform() == TargetPlatform.Web);
}
}
private boolean isFlexSdkLibrary(VirtualFile file, VirtualFile jarFile) {
if (flexmojosSdkHomePath != null) {
return file.getPath().startsWith(flexmojosSdkHomePath);
}
final String name = file.getName();
for (Pair<String, String> pair : FlexDefinitionMapProcessor.FLEX_LIBS_PATTERNS) {
if (name.startsWith(pair.first)) {
return libraryContains(pair.second, jarFile);
}
}
if (name.equals("textLayout.swc")) {
return libraryContains("flashx.textLayout.EditClasses", jarFile);
}
else if (name.equals("osmf.swc")) {
return libraryContains("org.osmf.utils.Version", jarFile);
}
// todo check and add
else if (name.startsWith("miglayout-")) {
return true;
}
return false;
}
private boolean libraryContains(String className, VirtualFile jarFile) {
return ActionScriptClassResolver
.findClassByQNameStatic(className, GlobalSearchScope.fileScope(module.getProject(), Library.getSwfFile(jarFile))) != null;
}
private void collectFromLibraryOrderEntry(VirtualFile[] files) {
for (VirtualFile jarFile : files) {
VirtualFile file = getRealFileIfValidSwc(jarFile);
if (file != null && !isAutomationOrUselessLibrary(file.getName())) {
addLibrary(jarFile, isFlexSdkLibrary(file, jarFile));
}
}
}
private void addLibrary(VirtualFile jarFile, boolean isFromFlexSdk) {
(isFromFlexSdk ? sdkLibraries : externalLibraries).add(libraryManager.createOriginalLibrary(jarFile, initializer));
}
// IDEA-74117
private static boolean isSwfAndCatalogExists(VirtualFile jarFile) {
if (Library.getSwfFile(jarFile) == null || Library.getCatalogFile(jarFile) == null) {
LogMessageUtil.LOG.warn("SWC is corrupted (library.swf or catalog.xml doesn't exists): " + jarFile.getPath());
return false;
}
return true;
}
}