001    /*
002     * Copyright 2010-2013 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.jet.cli.jvm.compiler;
018    
019    import com.intellij.codeInsight.ExternalAnnotationsManager;
020    import com.intellij.core.CoreApplicationEnvironment;
021    import com.intellij.core.CoreJavaFileManager;
022    import com.intellij.core.JavaCoreApplicationEnvironment;
023    import com.intellij.core.JavaCoreProjectEnvironment;
024    import com.intellij.lang.java.JavaParserDefinition;
025    import com.intellij.mock.MockApplication;
026    import com.intellij.mock.MockProject;
027    import com.intellij.openapi.Disposable;
028    import com.intellij.openapi.components.ServiceManager;
029    import com.intellij.openapi.extensions.Extensions;
030    import com.intellij.openapi.fileTypes.PlainTextFileType;
031    import com.intellij.openapi.project.Project;
032    import com.intellij.openapi.vfs.VirtualFile;
033    import com.intellij.psi.PsiElementFinder;
034    import com.intellij.psi.PsiFile;
035    import com.intellij.psi.PsiManager;
036    import com.intellij.psi.impl.compiled.ClsCustomNavigationPolicy;
037    import com.intellij.psi.impl.file.impl.JavaFileManager;
038    import org.jetbrains.annotations.NotNull;
039    import org.jetbrains.jet.CompilerModeProvider;
040    import org.jetbrains.jet.OperationModeProvider;
041    import org.jetbrains.jet.asJava.JavaElementFinder;
042    import org.jetbrains.jet.asJava.LightClassGenerationSupport;
043    import org.jetbrains.jet.cli.common.CLIConfigurationKeys;
044    import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation;
045    import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity;
046    import org.jetbrains.jet.cli.common.messages.MessageCollector;
047    import org.jetbrains.jet.cli.jvm.JVMConfigurationKeys;
048    import org.jetbrains.jet.config.CommonConfigurationKeys;
049    import org.jetbrains.jet.config.CompilerConfiguration;
050    import org.jetbrains.jet.lang.parsing.JetParserDefinition;
051    import org.jetbrains.jet.lang.parsing.JetScriptDefinitionProvider;
052    import org.jetbrains.jet.lang.psi.JetFile;
053    import org.jetbrains.jet.lang.resolve.java.JetFilesProvider;
054    import org.jetbrains.jet.lang.resolve.java.vfilefinder.VirtualFileFinder;
055    import org.jetbrains.jet.plugin.JetFileType;
056    import org.jetbrains.jet.utils.PathUtil;
057    
058    import java.io.File;
059    import java.util.ArrayList;
060    import java.util.List;
061    
062    import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.ERROR;
063    import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.WARNING;
064    
065    public class JetCoreEnvironment {
066    
067        private final JavaCoreApplicationEnvironment applicationEnvironment;
068        private final JavaCoreProjectEnvironment projectEnvironment;
069        private final List<JetFile> sourceFiles = new ArrayList<JetFile>();
070        private final ClassPath classPath = new ClassPath();
071    
072        private final CoreExternalAnnotationsManager annotationsManager;
073    
074        private final CompilerConfiguration configuration;
075    
076        public JetCoreEnvironment(Disposable parentDisposable, @NotNull CompilerConfiguration configuration) {
077            this.configuration = configuration.copy();
078            this.configuration.setReadOnly(true);
079    
080            this.applicationEnvironment = new JavaCoreApplicationEnvironment(parentDisposable);
081    
082            // ability to get text from annotations xml files
083            applicationEnvironment.registerFileType(PlainTextFileType.INSTANCE, "xml");
084    
085            applicationEnvironment.registerFileType(JetFileType.INSTANCE, "kt");
086            applicationEnvironment.registerFileType(JetFileType.INSTANCE, "kts");
087            applicationEnvironment.registerFileType(JetFileType.INSTANCE, "ktm");
088            applicationEnvironment.registerFileType(JetFileType.INSTANCE, JetParserDefinition.KTSCRIPT_FILE_SUFFIX); // should be renamed to kts
089            applicationEnvironment.registerFileType(JetFileType.INSTANCE, "jet");
090            applicationEnvironment.registerParserDefinition(new JavaParserDefinition());
091            applicationEnvironment.registerParserDefinition(new JetParserDefinition());
092    
093            applicationEnvironment.getApplication().registerService(OperationModeProvider.class, new CompilerModeProvider());
094    
095            projectEnvironment = new JavaCoreProjectEnvironment(parentDisposable, applicationEnvironment);
096    
097            MockProject project = projectEnvironment.getProject();
098            project.registerService(JetScriptDefinitionProvider.class, new JetScriptDefinitionProvider());
099            project.registerService(JetFilesProvider.class, new CliJetFilesProvider(this));
100            project.registerService(CoreJavaFileManager.class, (CoreJavaFileManager) ServiceManager.getService(project, JavaFileManager.class));
101    
102            CliLightClassGenerationSupport cliLightClassGenerationSupport = new CliLightClassGenerationSupport();
103            project.registerService(LightClassGenerationSupport.class, cliLightClassGenerationSupport);
104            project.registerService(CliLightClassGenerationSupport.class, cliLightClassGenerationSupport);
105    
106            Extensions.getArea(project)
107                    .getExtensionPoint(PsiElementFinder.EP_NAME)
108                    .registerExtension(new JavaElementFinder(project, cliLightClassGenerationSupport));
109    
110            // This extension point should be registered in JavaCoreApplicationEnvironment
111            CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), ClsCustomNavigationPolicy.EP_NAME,
112                                                              ClsCustomNavigationPolicy.class);
113    
114            annotationsManager = new CoreExternalAnnotationsManager(project.getComponent(PsiManager.class));
115            project.registerService(ExternalAnnotationsManager.class, annotationsManager);
116    
117            for (File path : configuration.getList(JVMConfigurationKeys.CLASSPATH_KEY)) {
118                addToClasspath(path);
119            }
120            for (File path : configuration.getList(JVMConfigurationKeys.ANNOTATIONS_PATH_KEY)) {
121                addExternalAnnotationsRoot(path);
122            }
123            for (String path : configuration.getList(CommonConfigurationKeys.SOURCE_ROOTS_KEY)) {
124                addSources(path);
125            }
126    
127            JetScriptDefinitionProvider.getInstance(project).addScriptDefinitions(
128                    configuration.getList(CommonConfigurationKeys.SCRIPT_DEFINITIONS_KEY));
129    
130            project.registerService(VirtualFileFinder.class, new CliVirtualFileFinder(classPath));
131        }
132    
133        public CompilerConfiguration getConfiguration() {
134            return configuration;
135        }
136    
137        @NotNull
138        public MockApplication getApplication() {
139            return applicationEnvironment.getApplication();
140        }
141    
142        @NotNull
143        public Project getProject() {
144            return projectEnvironment.getProject();
145        }
146    
147        private void addExternalAnnotationsRoot(File path) {
148            if (!path.exists()) {
149                report(WARNING, "Annotations path entry points to a non-existent location: " + path);
150                return;
151            }
152            annotationsManager.addExternalAnnotationsRoot(PathUtil.jarFileOrDirectoryToVirtualFile(path));
153        }
154    
155        private void addSources(File file) {
156            if (file.isDirectory()) {
157                File[] files = file.listFiles();
158                if (files != null) {
159                    for (File child : files) {
160                        addSources(child);
161                    }
162                }
163            }
164            else {
165                VirtualFile fileByPath = applicationEnvironment.getLocalFileSystem().findFileByPath(file.getAbsolutePath());
166                if (fileByPath != null) {
167                    PsiFile psiFile = PsiManager.getInstance(getProject()).findFile(fileByPath);
168                    if (psiFile instanceof JetFile) {
169                        sourceFiles.add((JetFile) psiFile);
170                    }
171                }
172            }
173        }
174    
175        private void addSources(String path) {
176            if (path == null) {
177                return;
178            }
179    
180            VirtualFile vFile = applicationEnvironment.getLocalFileSystem().findFileByPath(path);
181            if (vFile == null) {
182                report(ERROR, "Source file or directory not found: " + path);
183                return;
184            }
185            if (!vFile.isDirectory() && vFile.getFileType() != JetFileType.INSTANCE) {
186                report(ERROR, "Source entry is not a Kotlin file: " + path);
187                return;
188            }
189    
190            addSources(new File(path));
191        }
192    
193        private void addToClasspath(File path) {
194            if (path.isFile()) {
195                VirtualFile jarFile = applicationEnvironment.getJarFileSystem().findFileByPath(path + "!/");
196                if (jarFile == null) {
197                    report(WARNING, "Classpath entry points to a file that is not a JAR archive: " + path);
198                    return;
199                }
200                projectEnvironment.addJarToClassPath(path);
201                classPath.add(jarFile);
202            }
203            else {
204                VirtualFile root = applicationEnvironment.getLocalFileSystem().findFileByPath(path.getAbsolutePath());
205                if (root == null) {
206                    report(WARNING, "Classpath entry points to a non-existent location: " + path);
207                    return;
208                }
209                projectEnvironment.addSourcesToClasspath(root);
210                classPath.add(root);
211            }
212        }
213    
214        public List<JetFile> getSourceFiles() {
215            return sourceFiles;
216        }
217    
218        private void report(@NotNull CompilerMessageSeverity severity, @NotNull String message) {
219            MessageCollector messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY);
220            if (messageCollector != null) {
221                messageCollector.report(severity, message, CompilerMessageLocation.NO_LOCATION);
222            }
223            else {
224                throw new CompileEnvironmentException(message);
225            }
226        }
227    }