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.PathUtil; 026 import com.intellij.util.io.URLUtil; 027 import kotlin.Function1; 028 import kotlin.Function2; 029 import kotlin.Unit; 030 import org.jetbrains.annotations.NotNull; 031 import org.jetbrains.annotations.Nullable; 032 import org.jetbrains.kotlin.idea.JetFileType; 033 import org.jetbrains.kotlin.js.JavaScript; 034 import org.jetbrains.kotlin.psi.JetFile; 035 import org.jetbrains.kotlin.utils.KotlinJavascriptMetadata; 036 import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils; 037 import org.jetbrains.kotlin.utils.LibraryUtils; 038 039 import java.io.File; 040 import java.util.List; 041 042 import static org.jetbrains.kotlin.utils.LibraryUtils.*; 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 public List<String> getLibraries() { 072 return files; 073 } 074 075 @Override 076 protected void init(@NotNull final List<JetFile> sourceFilesInLibraries, @NotNull final List<KotlinJavascriptMetadata> metadata) { 077 if (files.isEmpty()) return; 078 079 final PsiManager psiManager = PsiManager.getInstance(getProject()); 080 081 Function1<String, Unit> report = new Function1<String, Unit>() { 082 @Override 083 public Unit invoke(String message) { 084 throw new IllegalStateException(message); 085 } 086 }; 087 088 Function2<String, VirtualFile, Unit> action = new Function2<String, VirtualFile, Unit>() { 089 @Override 090 public Unit invoke(String moduleName, VirtualFile file) { 091 if (moduleName != null) { 092 JetFileCollector jetFileCollector = new JetFileCollector(sourceFilesInLibraries, moduleName, psiManager); 093 VfsUtilCore.visitChildrenRecursively(file, jetFileCollector); 094 } 095 else { 096 String libraryPath = PathUtil.getLocalPath(file); 097 assert libraryPath != null : "libraryPath for " + file + " should not be null"; 098 metadata.addAll(KotlinJavascriptMetadataUtils.loadMetadata(libraryPath)); 099 } 100 101 return Unit.INSTANCE$; 102 } 103 }; 104 105 boolean hasErrors = checkLibFilesAndReportErrors(report, action); 106 assert !hasErrors : "hasErrors should be false"; 107 } 108 109 @Override 110 public boolean checkLibFilesAndReportErrors(@NotNull Function1<String, Unit> report) { 111 return checkLibFilesAndReportErrors(report, null); 112 } 113 114 private boolean checkLibFilesAndReportErrors(@NotNull Function1<String, Unit> report, @Nullable Function2<String, VirtualFile, Unit> action) { 115 if (files.isEmpty()) { 116 return false; 117 } 118 119 VirtualFileSystem fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL); 120 VirtualFileSystem jarFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JAR_PROTOCOL); 121 122 String moduleName = null; 123 124 for (String path : files) { 125 VirtualFile file; 126 if (path.charAt(0) == '@') { 127 moduleName = path.substring(1); 128 continue; 129 } 130 131 File filePath = new File(path); 132 if (!filePath.exists()) { 133 report.invoke("Path '" + path + "'does not exist"); 134 return true; 135 } 136 137 if (path.endsWith(".jar") || path.endsWith(".zip")) { 138 file = jarFileSystem.findFileByPath(path + URLUtil.JAR_SEPARATOR); 139 } 140 else { 141 file = fileSystem.findFileByPath(path); 142 } 143 144 if (file == null) { 145 report.invoke("File '" + path + "'does not exist or could not be read"); 146 return true; 147 } 148 else { 149 String actualModuleName; 150 151 if (moduleName != null) { 152 actualModuleName = moduleName; 153 } 154 else if (isOldKotlinJavascriptLibrary(filePath)) { 155 actualModuleName = LibraryUtils.getKotlinJsModuleName(filePath); 156 } 157 else if (isKotlinJavascriptLibraryWithMetadata(filePath)) { 158 actualModuleName = null; 159 } 160 else { 161 report.invoke("'" + path + "' is not a valid Kotlin Javascript library"); 162 return true; 163 } 164 165 if (action != null) { 166 action.invoke(actualModuleName, file); 167 } 168 } 169 moduleName = null; 170 } 171 172 return false; 173 } 174 175 protected static JetFile getJetFileByVirtualFile(VirtualFile file, String moduleName, PsiManager psiManager) { 176 PsiFile psiFile = psiManager.findFile(file); 177 assert psiFile != null; 178 179 setupPsiFile(psiFile, moduleName); 180 return (JetFile) psiFile; 181 } 182 183 protected static void setupPsiFile(PsiFile psiFile, String moduleName) { 184 psiFile.putUserData(EXTERNAL_MODULE_NAME, moduleName); 185 } 186 187 private static class JetFileCollector extends VirtualFileVisitor { 188 private final List<JetFile> jetFiles; 189 private final String moduleName; 190 private final PsiManager psiManager; 191 192 private JetFileCollector(List<JetFile> files, String name, PsiManager manager) { 193 moduleName = name; 194 psiManager = manager; 195 jetFiles = files; 196 } 197 198 @Override 199 public boolean visitFile(@NotNull VirtualFile file) { 200 if (!file.isDirectory() && StringUtil.notNullize(file.getExtension()).equalsIgnoreCase(JetFileType.EXTENSION)) { 201 jetFiles.add(getJetFileByVirtualFile(file, moduleName, psiManager)); 202 } 203 return true; 204 } 205 } 206 }