001 /* 002 * Copyright 2010-2015 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package org.jetbrains.kotlin.js.config; 018 019 import com.intellij.openapi.project.Project; 020 import com.intellij.openapi.util.Key; 021 import com.intellij.openapi.util.text.StringUtil; 022 import com.intellij.openapi.vfs.*; 023 import com.intellij.psi.PsiFile; 024 import com.intellij.psi.PsiManager; 025 import com.intellij.util.io.URLUtil; 026 import kotlin.Function1; 027 import kotlin.Function2; 028 import kotlin.Unit; 029 import org.jetbrains.annotations.NotNull; 030 import org.jetbrains.annotations.Nullable; 031 import org.jetbrains.kotlin.js.JavaScript; 032 import org.jetbrains.kotlin.idea.JetFileType; 033 import org.jetbrains.kotlin.psi.JetFile; 034 import org.jetbrains.kotlin.utils.LibraryUtils; 035 036 import java.io.File; 037 import java.util.ArrayList; 038 import java.util.Collections; 039 import java.util.List; 040 041 import static org.jetbrains.kotlin.utils.LibraryUtils.isKotlinJavascriptLibrary; 042 import static org.jetbrains.kotlin.utils.LibraryUtils.isKotlinJavascriptStdLibrary; 043 044 public class LibrarySourcesConfig extends Config { 045 @NotNull 046 public static final Key<String> EXTERNAL_MODULE_NAME = Key.create("externalModule"); 047 @NotNull 048 public static final String UNKNOWN_EXTERNAL_MODULE_NAME = "<unknown>"; 049 050 public static final String STDLIB_JS_MODULE_NAME = "stdlib"; 051 public static final String BUILTINS_JS_MODULE_NAME = "builtins"; 052 public static final String BUILTINS_JS_FILE_NAME = BUILTINS_JS_MODULE_NAME + JavaScript.DOT_EXTENSION; 053 public static final String STDLIB_JS_FILE_NAME = STDLIB_JS_MODULE_NAME + JavaScript.DOT_EXTENSION; 054 055 @NotNull 056 private final List<String> files; 057 058 public LibrarySourcesConfig( 059 @NotNull Project project, 060 @NotNull String moduleId, 061 @NotNull List<String> files, 062 @NotNull EcmaVersion ecmaVersion, 063 boolean sourcemap, 064 boolean inlineEnabled 065 ) { 066 super(project, moduleId, ecmaVersion, sourcemap, inlineEnabled); 067 this.files = files; 068 } 069 070 @NotNull 071 @Override 072 protected List<JetFile> generateLibFiles() { 073 if (files.isEmpty()) { 074 return Collections.emptyList(); 075 } 076 077 final List<JetFile> jetFiles = new ArrayList<JetFile>(); 078 final PsiManager psiManager = PsiManager.getInstance(getProject()); 079 080 Function1<String, Unit> report = new Function1<String, Unit>() { 081 @Override 082 public Unit invoke(String message) { 083 throw new IllegalStateException(message); 084 } 085 }; 086 087 Function2<String, VirtualFile, Unit> action = new Function2<String, VirtualFile, Unit>() { 088 @Override 089 public Unit invoke(String moduleName, VirtualFile file) { 090 JetFileCollector jetFileCollector = new JetFileCollector(jetFiles, moduleName, psiManager); 091 VfsUtilCore.visitChildrenRecursively(file, jetFileCollector); 092 return Unit.INSTANCE$; 093 } 094 }; 095 096 boolean hasErrors = checkLibFilesAndReportErrors(report, action); 097 assert !hasErrors : "hasErrors should be false"; 098 099 return jetFiles; 100 } 101 102 @Override 103 public boolean checkLibFilesAndReportErrors(@NotNull Function1<String, Unit> report) { 104 return checkLibFilesAndReportErrors(report, null); 105 } 106 107 private boolean checkLibFilesAndReportErrors(@NotNull Function1<String, Unit> report, @Nullable Function2<String, VirtualFile, Unit> action) { 108 if (files.isEmpty()) { 109 return false; 110 } 111 112 VirtualFileSystem fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL); 113 VirtualFileSystem jarFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JAR_PROTOCOL); 114 115 String moduleName = null; 116 117 for (String path : files) { 118 VirtualFile file; 119 String actualModuleName = moduleName; 120 if (path.charAt(0) == '@') { 121 moduleName = path.substring(1); 122 continue; 123 } 124 125 File filePath = new File(path); 126 if (!filePath.exists()) { 127 report.invoke("Path '" + path + "'does not exist"); 128 return true; 129 } 130 131 if (path.endsWith(".jar") || path.endsWith(".zip")) { 132 file = jarFileSystem.findFileByPath(path + URLUtil.JAR_SEPARATOR); 133 } 134 else { 135 file = fileSystem.findFileByPath(path); 136 } 137 138 if (file == null) { 139 report.invoke("File '" + path + "'does not exist or could not be read"); 140 return true; 141 } 142 else { 143 if (isKotlinJavascriptStdLibrary(filePath)) { 144 actualModuleName = STDLIB_JS_MODULE_NAME; 145 } 146 else if (isKotlinJavascriptLibrary(filePath)) { 147 actualModuleName = LibraryUtils.getKotlinJsModuleName(filePath); 148 } 149 else if (actualModuleName == null) { 150 report.invoke("'" + path + "' is not a valid Kotlin Javascript library"); 151 return true; 152 } 153 if (actualModuleName == null) { 154 report.invoke("Could not find " + LibraryUtils.KOTLIN_JS_MODULE_NAME + " for '" + path + "'"); 155 return true; 156 } 157 if (action != null) { 158 action.invoke(actualModuleName, file); 159 } 160 } 161 moduleName = null; 162 } 163 164 return false; 165 } 166 167 protected JetFile getJetFileByVirtualFile(VirtualFile file, String moduleName, PsiManager psiManager) { 168 PsiFile psiFile = psiManager.findFile(file); 169 assert psiFile != null; 170 171 setupPsiFile(psiFile, moduleName); 172 return (JetFile) psiFile; 173 } 174 175 protected static void setupPsiFile(PsiFile psiFile, String moduleName) { 176 psiFile.putUserData(EXTERNAL_MODULE_NAME, moduleName); 177 } 178 179 private class JetFileCollector extends VirtualFileVisitor { 180 private final List<JetFile> jetFiles; 181 private final String moduleName; 182 private final PsiManager psiManager; 183 184 private JetFileCollector(List<JetFile> files, String name, PsiManager manager) { 185 moduleName = name; 186 psiManager = manager; 187 jetFiles = files; 188 } 189 190 @Override 191 public boolean visitFile(@NotNull VirtualFile file) { 192 if (!file.isDirectory() && StringUtil.notNullize(file.getExtension()).equalsIgnoreCase(JetFileType.EXTENSION)) { 193 jetFiles.add(getJetFileByVirtualFile(file, moduleName, psiManager)); 194 } 195 return true; 196 } 197 } 198 }