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.types.lang.KotlinBuiltIns;
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    
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    }