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