diff --git a/.idea/inspectionProfiles/idea_default_no_spellchecker.xml b/.idea/inspectionProfiles/idea_default_ci.xml similarity index 99% rename from .idea/inspectionProfiles/idea_default_no_spellchecker.xml rename to .idea/inspectionProfiles/idea_default_ci.xml index 1c3650364a3e..3bec5d25bea5 100644 --- a/.idea/inspectionProfiles/idea_default_no_spellchecker.xml +++ b/.idea/inspectionProfiles/idea_default_ci.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/aether-dependency-resolver/src/org/jetbrains/idea/maven/aether/ArtifactRepositoryManager.java b/aether-dependency-resolver/src/org/jetbrains/idea/maven/aether/ArtifactRepositoryManager.java index fabe4b234786..27ae08c74b94 100644 --- a/aether-dependency-resolver/src/org/jetbrains/idea/maven/aether/ArtifactRepositoryManager.java +++ b/aether-dependency-resolver/src/org/jetbrains/idea/maven/aether/ArtifactRepositoryManager.java @@ -14,6 +14,7 @@ import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.*; import org.eclipse.aether.spi.connector.RepositoryConnectorFactory; import org.eclipse.aether.spi.connector.transport.TransporterFactory; +import org.eclipse.aether.transfer.TransferCancelledException; import org.eclipse.aether.transfer.TransferEvent; import org.eclipse.aether.transfer.TransferListener; import org.eclipse.aether.transport.file.FileTransporterFactory; @@ -87,11 +88,16 @@ public class ArtifactRepositoryManager { if (progressConsumer != ProgressConsumer.DEAF) { session.setTransferListener((TransferListener)Proxy .newProxyInstance(session.getClass().getClassLoader(), new Class[]{TransferListener.class}, new InvocationHandler() { + private final EnumSet checkCancelEvents = EnumSet.of(TransferEvent.EventType.INITIATED, TransferEvent.EventType.STARTED, TransferEvent.EventType.PROGRESSED); public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { final Object event = args[0]; if (event instanceof TransferEvent) { + final TransferEvent.EventType type = ((TransferEvent)event).getType(); + if (checkCancelEvents.contains(type) && progressConsumer.isCanceled()) { + throw new TransferCancelledException(); + } progressConsumer.consume(event.toString()); - //if (((TransferEvent)event).getType() != TransferEvent.EventType.PROGRESSED) { + //if (type != TransferEvent.EventType.PROGRESSED) { // progressConsumer.consume(event.toString()); //} } diff --git a/aether-dependency-resolver/src/org/jetbrains/idea/maven/aether/ProgressConsumer.java b/aether-dependency-resolver/src/org/jetbrains/idea/maven/aether/ProgressConsumer.java index c59606bcee2a..f77f0586a3bc 100644 --- a/aether-dependency-resolver/src/org/jetbrains/idea/maven/aether/ProgressConsumer.java +++ b/aether-dependency-resolver/src/org/jetbrains/idea/maven/aether/ProgressConsumer.java @@ -11,4 +11,8 @@ public interface ProgressConsumer { }; void consume(String message); + + default boolean isCanceled() { + return false; + } } diff --git a/bin/idea.properties b/bin/idea.properties index edf377ffeab2..71bd2f92e4da 100644 --- a/bin/idea.properties +++ b/bin/idea.properties @@ -68,12 +68,6 @@ idea.dynamic.classpath=false #--------------------------------------------------------------------- idea.popup.weight=heavy -#--------------------------------------------------------------------- -# Disabling this property may lead to visual glitches like blinking and fail to repaint -# on certain display adapter cards. -#--------------------------------------------------------------------- -sun.java2d.noddraw=true - #--------------------------------------------------------------------- # Removing this property may lead to editor performance degradation under Windows. #--------------------------------------------------------------------- diff --git a/build/dependencies/build.gradle b/build/dependencies/build.gradle index fea20ebb9c30..d250a1031054 100644 --- a/build/dependencies/build.gradle +++ b/build/dependencies/build.gradle @@ -23,6 +23,13 @@ buildscript { } } +apply plugin: 'idea' +idea { + module { + inheritOutputDirs true + } +} + apply plugin: "de.undercouch.download" apply plugin: 'base' @@ -30,9 +37,16 @@ group 'com.intellij.build' version '1.0' try { - project.ext.inJetBrainsNetwork = InetAddress.getByName("repo.labs.intellij.net").isReachable(300) + project.ext.inJetBrainsNetwork = InetAddress.getByName("repo.labs.intellij.net").isReachable(1000) + if (!project.ext.inJetBrainsNetwork && org.gradle.internal.os.OperatingSystem.current().isWindows()) { + project.ext.inJetBrainsNetwork = Runtime.getRuntime().exec("ping -n 1 repo.labs.intellij.net").waitFor() == 0 + } + if (!project.ext.inJetBrainsNetwork) { + logger.info('repo.labs.intellij.net is not reachable') + } } -catch (UnknownHostException ignore) { +catch (UnknownHostException e) { + logger.info('repo.labs.intellij.net is not reachable', e) project.ext.inJetBrainsNetwork = false } diff --git a/build/dependencies/dependencies.iml b/build/dependencies/dependencies.iml index e32a091ed56f..d028e590588b 100644 --- a/build/dependencies/dependencies.iml +++ b/build/dependencies/dependencies.iml @@ -1,8 +1,6 @@ - - - + diff --git a/build/dependencies/setupJbre.gradle b/build/dependencies/setupJbre.gradle index 594d3b5af5af..d3235c4595d9 100644 --- a/build/dependencies/setupJbre.gradle +++ b/build/dependencies/setupJbre.gradle @@ -27,9 +27,15 @@ jrePlatformsToDownload(targetOs).each { platform -> if (org.gradle.internal.os.OperatingSystem.current().isWindows()) { task("untarJbrex_${platform}_$arch", type: Copy, dependsOn: "downloadJbrex_${platform}_$arch") { def downloadOutputs = tasks["downloadJbrex_${platform}_$arch"].outputs.files - from tarTree(downloadOutputs.singleFile) + from tarTree(downloadOutputs.singleFile), { + exclude 'lib/tools.jar' + } + from tarTree(downloadOutputs.singleFile), { + include 'lib/tools.jar' + into 'jre' + } into { "${downloadOutputs.singleFile.parent}/${downloadOutputs.singleFile.name - '.tar.gz'}/" } - rename('lib/tools.jar', 'jre/lib/tools.jar') + includeEmptyDirs = false } } else { @@ -61,12 +67,14 @@ def createTarJbreTask(String taskName, String untarTaskName, String platform, St if (org.gradle.internal.os.OperatingSystem.current().isWindows()) { task(taskName, type: Tar) { def untarOutputs = tasks[untarTaskName].outputs.files + inputs.file(untarOutputs) from "$untarOutputs.singleFile/$dirToTar" if (!includeToolsJar) { exclude "**/tools.jar" } + into dirToTar compression = Compression.GZIP - archiveName = "${archiveName}.tar.gz" + setArchiveName("${archiveName}.tar.gz") destinationDir = untarOutputs.singleFile.parentFile } } diff --git a/build/dependencies/setupJdk.gradle b/build/dependencies/setupJdk.gradle index c41952f50a05..8736f2627f21 100644 --- a/build/dependencies/setupJdk.gradle +++ b/build/dependencies/setupJdk.gradle @@ -26,7 +26,7 @@ project.ext.targetOs = "${System.getProperty("intellij.build.target.os", "all")} task downloadJdk18 { def os = org.gradle.internal.os.OperatingSystem.current() - def arch = !os.isWindows() || !os.nativePrefix.endsWith('x86') ? 'x64' : 'x86' + def arch = !os.isWindows() || System.getenv("ProgramFiles(x86)") != null ? 'x64' : 'x86' def platform = currentPlatformName() def artifactName = "jbsdk8${jdkBuild}_${platform}_${arch}.tar.gz" @@ -60,17 +60,22 @@ task setupJdk18(dependsOn: downloadJdk18) { def unpackJdk(jdkArchive, outputDir) { logger.info("Unpacking $jdkArchive to ${outputDir}") file(outputDir).deleteDir() - copy { - from tarTree(jdkArchive) - into outputDir - } - def jdkContents = file("${outputDir}/jdk/Contents") - if (org.gradle.internal.os.OperatingSystem.current().isMacOsX() && jdkContents.exists()) { + def currentOs = org.gradle.internal.os.OperatingSystem.current() + if (currentOs.isWindows()) { copy { - from fileTree(jdkContents) + from tarTree(jdkArchive) into outputDir } - jdkContents.deleteDir() + } + else { + exec { commandLine 'mkdir', '-p', outputDir } + exec { commandLine 'tar', 'xpf', "$jdkArchive.absolutePath", '--directory', outputDir } + def jdkContents = file("${outputDir}/jdk/Contents") + if (currentOs.isMacOsX() && jdkContents.exists()) { + jdkContents.listFiles().each { file -> + exec { commandLine 'mv', "$file.absolutePath", outputDir } + } + } } } @@ -114,9 +119,10 @@ task setupJdk16(dependsOn: configurations.jdk16) { doLast { logger.info("Setting up JDK 1.6 to compile") - unpackJdk(jdkArchive, outputDir) + unpackJdk(jdkArchive.call(), outputDir) } } +setupJdk16.onlyIf { inJetBrainsNetwork } def intellijProjectDir() { def communityDir = project.file("../..") @@ -126,7 +132,7 @@ def intellijProjectDir() { static def jdk16Version() { def os = org.gradle.internal.os.OperatingSystem.current() if (os.isWindows()) { - def suffix = os.nativePrefix.endsWith('x86') ? "_i586" : "x64" + def suffix = System.getenv("ProgramFiles(x86)") != null ? "x64" : "i586" return "6.45:windows_$suffix" } else if (os.isMacOsX()) { diff --git a/build/groovy/org/jetbrains/intellij/build/BaseIdeaProperties.groovy b/build/groovy/org/jetbrains/intellij/build/BaseIdeaProperties.groovy index bebed024e0f9..837dd3668a0d 100644 --- a/build/groovy/org/jetbrains/intellij/build/BaseIdeaProperties.groovy +++ b/build/groovy/org/jetbrains/intellij/build/BaseIdeaProperties.groovy @@ -1,20 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + + + package org.jetbrains.intellij.build import org.jetbrains.intellij.build.impl.PlatformLayout @@ -69,7 +57,8 @@ abstract class BaseIdeaProperties extends ProductProperties { "remote-servers-java-impl", "testFramework", "tests_bootstrap", - "ui-designer-core" + "ui-designer-core", + "uast-java", ] protected static final List BUNDLED_PLUGIN_MODULES = [ "copyright", "properties", "terminal", "editorconfig", "settings-repository", "yaml", diff --git a/build/groovy/org/jetbrains/intellij/build/CommunityStandaloneJpsBuilder.groovy b/build/groovy/org/jetbrains/intellij/build/CommunityStandaloneJpsBuilder.groovy index 894bd42e4a4f..9624e155779c 100644 --- a/build/groovy/org/jetbrains/intellij/build/CommunityStandaloneJpsBuilder.groovy +++ b/build/groovy/org/jetbrains/intellij/build/CommunityStandaloneJpsBuilder.groovy @@ -110,6 +110,7 @@ class CommunityStandaloneJpsBuilder { include(name: "commons-logging-*.jar") include(name: "httpclient-*.jar") include(name: "httpcore-*.jar") + include(name: "slf4j-api-*.jar") } ant.fileset(dir: "$home/jps/lib") { include(name: "optimizedFileManager.jar") diff --git a/build/order.txt b/build/order.txt index 2bb3f9d04790..7cb713b930cd 100644 --- a/build/order.txt +++ b/build/order.txt @@ -1335,7 +1335,7 @@ messages/VcsBundle.properties:/lib/resources_en.jar com/intellij/openapi/vcs/changes/VetoSavingCommittingDocumentsAdapter.class:/lib/idea.jar com/intellij/openapi/fileEditor/FileDocumentSynchronizationVetoListener.class:/lib/openapi.jar com/intellij/openapi/vcs/changes/VcsDirtyScopeVfsListener.class:/lib/idea.jar -com/intellij/openapi/vfs/VirtualFileAdapter.class:/lib/openapi.jar +com/intellij/openapi/vfs/VirtualFileListener.class:/lib/openapi.jar com/intellij/openapi/vfs/VirtualFilePropertyEvent.class:/lib/openapi.jar com/intellij/openapi/vfs/VirtualFileMoveEvent.class:/lib/openapi.jar com/intellij/openapi/vcs/FilePath.class:/lib/openapi.jar diff --git a/build/scripts/common_tests.gant b/build/scripts/common_tests.gant deleted file mode 100644 index 898b8938f613..000000000000 --- a/build/scripts/common_tests.gant +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2000-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import static org.jetbrains.jps.idea.IdeaProjectLoader.guessHome - -includeTargets << new File("${guessHome(this)}/build/scripts/utils.gant") - -requireProperty("out", "$home/out") - -target(compile: "Compile project") { - ant.delete(failonerror: false) { - fileset(dir: "$home/reports") - fileset(dir: "$home/bin", includes: "*.hprof") - } - - loadProject() - prepareOutputFolder() - - if (System.getProperty("tests.only")) { - ant.delete(dir: "$home/out") - ant.unzip(src: "$home/out.zip", dest: "$home/out") - return - } - - if (!isDefined("avoid.project.rebuild")) { - projectBuilder.cleanOutput() - projectBuilder.buildAll() - } -} - -private pass(List args, String prop) { - if (isDefined(prop)) { - args << "-D$prop=${p(prop)}" - } -} - -target('run_tests': 'Run java tests') { - depends([compile]) - - def classpathFile = "$home/junit.classpath" - //todo[nik] currently runtime classpath includes path to JDKs from all modules in it so we need to manually exclude paths from JDK 1.6 - List testRuntimeClasspath = removeJdkJarFiles(projectBuilder.moduleRuntimeClasspath(findModule("community-main"), true)) - testRuntimeClasspath << "${jdk8Home}/lib/tools.jar" - projectBuilder.info("Test runtime classpath=" + testRuntimeClasspath) - new File(classpathFile).text = testRuntimeClasspath.findAll({ new File((String)it).exists() }).join('\n') - - List bootstrapClasspath = removeJdkJarFiles(projectBuilder.moduleRuntimeClasspath(findModule("tests_bootstrap"), false)) - bootstrapClasspath << "${jdk8Home}/lib/tools.jar" - projectBuilder.info("Bootstrap classpath=" + bootstrapClasspath) - - testcases.each { testCase -> - List jvmArgs = [ - "-Dclasspath.file=${classpathFile}", - "-Didea.platform.prefix=Idea", - "-Dbootstrap.testcases=$testCase" - ] - - [ - "idea.test.group", - "idea.test.patterns", - "idea.coverage.enabled.build", - "teamcity.tests.recentlyFailedTests.file" - ].each { pass(jvmArgs, it) } - - def tempDir = p("teamcity.build.tempDir", p("java.io.tmpdir")) - jvmArgs << "-Djava.io.tmpdir=${tempDir}" - jvmArgs << "-Dteamcity.build.tempDir=${tempDir}" - - System.getProperties().entrySet().each { - if (it.key.startsWith("pass.")) { - def trimmed = it.key.substring("pass.".length()); - jvmArgs << "-D${trimmed}=${it.value}" - }; - } - jvmArgs.addAll(commonJvmArgsForTests()) - if (isDefined("jvm_args")) { - jvmArgs.addAll(jvm_args) - } - - projectBuilder.info("Starting JUnit $testCase, JVM options: $jvmArgs") - ant.junit(fork: "yes", showoutput: "true", logfailedtests: false) { - jvmArgs.each { - jvmarg(value: it) - } - - classpath { - bootstrapClasspath.each { - pathelement(location: it) - } - } - - test(name: 'com.intellij.tests.BootstrapTests') - } - } -} - -target('default' : "Run all tests") { - depends([compile, run_tests]) -} \ No newline at end of file diff --git a/build/scripts/unpackJdks.gant b/build/scripts/unpackJdks.gant index b36928ed00a8..d938fcc6b960 100644 --- a/build/scripts/unpackJdks.gant +++ b/build/scripts/unpackJdks.gant @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import org.apache.tools.ant.BuildException target(default: "Unpack JDKs") { def workingDirectory = getDependenciesProjectDir() @@ -24,13 +25,14 @@ target(default: "Unpack JDKs") { process.consumeProcessErrorStream((OutputStream)System.err) process.waitFor() if (process.exitValue() != 0) { - projectBuilder.error("Cannot unpack JDK") + throw new BuildException("Cannot unpack JDK") } } private File getDependenciesProjectDir() { String home = guessHome(this) - return new File(home, new File(home).name == "community" ? "build/dependencies" : "community/build/dependencies") + File community = new File(home, 'community') + return new File(community.exists() ? community : new File(home), "build/dependencies") } static String guessHome(Script script) { diff --git a/build/scripts/utils.gant b/build/scripts/utils.gant index bfb2918f2691..974307edc841 100644 --- a/build/scripts/utils.gant +++ b/build/scripts/utils.gant @@ -56,10 +56,6 @@ binding.setVariable("includeFile", {String filePath -> s }) -binding.setVariable("yjpOptions", { String sessionName, String platformSuffix = "" -> - "-agentlib:yjpagent$platformSuffix=probe_disable=*,disablealloc,disabletracing,onlylocal,disableexceptiontelemetry,delay=10000,sessionname=$sessionName".trim() -}) - binding.setVariable("isDefined", {String key -> try { this[key] @@ -239,15 +235,6 @@ private void setPathVariable(String name, String value) { pathVars.addPathVariable(name, value) } -binding.setVariable("prepareOutputFolder", { - def targetFolder = projectBuilder.buildIncrementally ? "$home/out/incremental-build" : out - projectBuilder.targetFolder = targetFolder - if (projectBuilder.buildIncrementally && Boolean.parseBoolean(p("jps.build.clear.incremental.caches", "false"))) { - FileUtil.delete(new File(targetFolder)) - FileUtil.delete(projectBuilder.dataStorageRoot) - } -}) - binding.setVariable("findModule", {String name -> project.modules.find { it.name == name } }) @@ -287,47 +274,6 @@ binding.setVariable("notifyArtifactBuilt", { String artifactPath -> projectBuilder.info("##teamcity[publishArtifacts '$relativePath']") }) -def debugPort = System.getProperty("debug.port") -def debugSuspend = System.getProperty("debug.suspend") ?: "n" -if (debugSuspend == 'y') { - println """ - -------------->------------- The process suspended until remote debugger connects to debug port -------------<------------- ----------------------------------------^------^------^------^------^------^------^---------------------------------------- - -""" -} - -binding.setVariable("commonJvmArgsForTests", { - def tempDir = p("teamcity.build.tempDir", p("java.io.tmpdir")) - def vmOptions = [ - "-ea", - "-Dfile.encoding=UTF-8", - "-Dio.netty.leakDetectionLevel=PARANOID", -// yjpOptions("junit", "64"), // uncomment to start tests with a profiling agent (e.g. in a personal build) - "-server", - "-Xbootclasspath/p:${projectBuilder.moduleOutput(findModule("boot"))}", - "-XX:+HeapDumpOnOutOfMemoryError", - "-Didea.home.path=$home", - "-Didea.config.path=$tempDir/config", - "-Didea.system.path=$tempDir/system", - "-XX:ReservedCodeCacheSize=300m", - "-XX:SoftRefLRUPolicyMSPerMB=50", - "-XX:+UseConcMarkSweepGC", - "-XX:-OmitStackTraceInFastThrow", - ] - if (shouldAddDebugOptions()) { - def jdwp = "-Xrunjdwp:transport=dt_socket,server=y,suspend=$debugSuspend" - if (debugPort != null) jdwp += ",address=$debugPort" - vmOptions.addAll(["-Xdebug", jdwp]) - } - return vmOptions -}) - -private static boolean shouldAddDebugOptions() { - return "true" != System.getProperty("idea.performance.tests") -} - binding.setVariable("forceDelete", { String dirPath -> // if wasn't deleted - retry several times attempt = 1 diff --git a/community-main.iml b/community-main.iml index 2d6c5c64c1bf..8f57ec84e1cc 100644 --- a/community-main.iml +++ b/community-main.iml @@ -100,7 +100,6 @@ - diff --git a/images/src/org/intellij/images/editor/impl/ImageEditorImpl.java b/images/src/org/intellij/images/editor/impl/ImageEditorImpl.java index c4f70f950994..f9c7e411a122 100644 --- a/images/src/org/intellij/images/editor/impl/ImageEditorImpl.java +++ b/images/src/org/intellij/images/editor/impl/ImageEditorImpl.java @@ -49,7 +49,7 @@ public final class ImageEditorImpl implements ImageEditor { editorUI = new ImageEditorUI(this); Disposer.register(this, editorUI); - VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() { + VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileListener() { @Override public void propertyChanged(@NotNull VirtualFilePropertyEvent event) { ImageEditorImpl.this.propertyChanged(event); diff --git a/images/src/org/intellij/images/thumbnail/impl/ThumbnailViewUI.java b/images/src/org/intellij/images/thumbnail/impl/ThumbnailViewUI.java index e5e8f2d271eb..91c854834d19 100644 --- a/images/src/org/intellij/images/thumbnail/impl/ThumbnailViewUI.java +++ b/images/src/org/intellij/images/thumbnail/impl/ThumbnailViewUI.java @@ -559,7 +559,7 @@ final class ThumbnailViewUI extends JPanel implements DataProvider, Disposable { } } - private final class VFSListener extends VirtualFileAdapter { + private final class VFSListener implements VirtualFileListener { public void contentsChanged(@NotNull VirtualFileEvent event) { VirtualFile file = event.getFile(); if (list != null) { diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java index f0d47c4e1a1d..560e9be70391 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java @@ -56,12 +56,16 @@ class CompilerReferenceReader { @Nullable TIntHashSet findReferentFileIds(@NotNull LightRef ref, boolean checkBaseClassAmbiguity) throws StorageException { - LightRef.LightClassHierarchyElementDef hierarchyElement = ref instanceof LightRef.LightClassHierarchyElementDef ? - (LightRef.LightClassHierarchyElementDef)ref : - ((LightRef.LightMember)ref).getOwner(); - TIntHashSet set = new TIntHashSet(); - final LightRef.NamedLightRef[] hierarchy = getWholeHierarchy(hierarchyElement, checkBaseClassAmbiguity); + LightRef.NamedLightRef[] hierarchy; + if (ref instanceof LightRef.LightClassHierarchyElementDef) { + hierarchy = new LightRef.NamedLightRef[]{(LightRef.NamedLightRef)ref}; + } + else { + LightRef.LightClassHierarchyElementDef hierarchyElement = ((LightRef.LightMember)ref).getOwner(); + hierarchy = getWholeHierarchy(hierarchyElement, checkBaseClassAmbiguity); + } if (hierarchy == null) return null; + TIntHashSet set = new TIntHashSet(); for (LightRef.NamedLightRef aClass : hierarchy) { final LightRef overriderUsage = ref.override(aClass.getName()); addUsages(overriderUsage, set); diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java index 677574074126..e23f8d0274f1 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java @@ -292,7 +292,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp LanguageLightRefAdapter adapter = null; if (isConstructorSuggestion) { adapter = ReadAction.compute(() -> LanguageLightRefAdapter.findAdapter(element)); - if (!adapter.isClass(element)) { + if (adapter == null || !adapter.isClass(element)) { return null; } } diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/DirtyScopeHolder.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/DirtyScopeHolder.java index 199219b60dfd..2287517717f0 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/DirtyScopeHolder.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/DirtyScopeHolder.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -212,7 +212,7 @@ public class DirtyScopeHolder extends UserDataHolderBase { } void installVFSListener() { - VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() { + VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileListener() { @Override public void fileCreated(@NotNull VirtualFileEvent event) { processChange(event.getFile()); diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java b/java/compiler/impl/src/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java index d1b23ec763e5..fc1ae4653b4e 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java @@ -116,7 +116,7 @@ public class TranslatingCompilerFilesMonitor { return false; } - private static class MyVfsListener extends VirtualFileAdapter { + private static class MyVfsListener implements VirtualFileListener { @Override public void propertyChanged(@NotNull final VirtualFilePropertyEvent event) { if (VirtualFile.PROP_NAME.equals(event.getPropertyName())) { diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactManagerImpl.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactManagerImpl.java index 58d46f32edc2..38c548a0ea28 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactManagerImpl.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactManagerImpl.java @@ -304,14 +304,6 @@ public class ArtifactManagerImpl extends ArtifactManager implements ProjectCompo } } - @Override - public void projectOpened() { - } - - @Override - public void projectClosed() { - } - @Override public Artifact[] getSortedArtifacts() { return myModel.getSortedArtifacts(); diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactVirtualFileListener.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactVirtualFileListener.java index cbe7922b2a95..5e6bc06a8eb4 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactVirtualFileListener.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactVirtualFileListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.MultiValuesMap; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileAdapter; +import com.intellij.openapi.vfs.VirtualFileListener; import com.intellij.openapi.vfs.VirtualFileMoveEvent; import com.intellij.openapi.vfs.VirtualFilePropertyEvent; import com.intellij.packaging.artifacts.Artifact; @@ -36,7 +36,7 @@ import java.util.Collection; /** * @author nik */ -public class ArtifactVirtualFileListener extends VirtualFileAdapter { +public class ArtifactVirtualFileListener implements VirtualFileListener { private final CachedValue> myParentPathsToArtifacts; private final ArtifactManagerImpl myArtifactManager; diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java index b3c1b492fcd9..99f91a1ddda6 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java @@ -151,7 +151,7 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV myValueDescriptor.updateRepresentation(myEvaluationContext, new DescriptorLabelListener() { @Override public void labelChanged() { - Icon nodeIcon = DebuggerTreeRenderer.getValueIcon(myValueDescriptor); + Icon nodeIcon = DebuggerTreeRenderer.getValueIcon(myValueDescriptor, myParent != null ? myParent.getDescriptor() : null); final String value = getValueString(); @SuppressWarnings("ThrowableResultOfMethodCallIgnored") EvaluateException exception = myValueDescriptor.getEvaluateException(); diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java index 808a2fd701a5..cc0248b49442 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java @@ -25,6 +25,7 @@ import com.intellij.debugger.impl.DebuggerUtilsEx; import com.intellij.debugger.jdi.VirtualMachineProxyImpl; import com.intellij.debugger.requests.ClassPrepareRequestor; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; @@ -203,18 +204,26 @@ public class PositionManagerImpl implements PositionManager, MultiRequestPositio return new JavaSourcePosition(sourcePosition, location.declaringType(), method, lambdaOrdinal); } - private static class JavaSourcePosition extends RemappedSourcePosition { + public static class JavaSourcePosition extends RemappedSourcePosition { private final String myExpectedClassName; private final String myExpectedMethodName; private final int myLambdaOrdinal; - public JavaSourcePosition(SourcePosition delegate, ReferenceType declaringType, Method method, int lambdaOrdinal) { + public JavaSourcePosition(@NotNull SourcePosition delegate, ReferenceType declaringType, Method method, int lambdaOrdinal) { super(delegate); myExpectedClassName = declaringType != null ? declaringType.name() : null; myExpectedMethodName = method != null ? method.name() : null; myLambdaOrdinal = lambdaOrdinal; } + public JavaSourcePosition(@NotNull SourcePosition delegate, int lambdaOrdinal) { + super(delegate); + assert lambdaOrdinal > -1; + myExpectedClassName = null; + myExpectedMethodName = "lambda$"; // fake lambda name + myLambdaOrdinal = lambdaOrdinal; + } + private PsiElement remapElement(PsiElement element) { String name = JVMNameUtil.getClassVMName(getEnclosingClass(element)); if (name != null && !name.equals(myExpectedClassName)) { @@ -244,37 +253,34 @@ public class PositionManagerImpl implements PositionManager, MultiRequestPositio @Override public SourcePosition mapDelegate(final SourcePosition original) { - return ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public SourcePosition compute() { - PsiFile file = original.getFile(); - int line = original.getLine(); - if (DebuggerUtilsEx.isLambdaName(myExpectedMethodName) && myLambdaOrdinal > -1) { - List lambdas = DebuggerUtilsEx.collectLambdas(original, true); + return ReadAction.compute(() -> { + PsiFile file = original.getFile(); + int line = original.getLine(); + if (DebuggerUtilsEx.isLambdaName(myExpectedMethodName) && myLambdaOrdinal > -1) { + List lambdas = DebuggerUtilsEx.collectLambdas(original, true); - Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file); - if (document == null || line >= document.getLineCount()) { - return original; - } - if (myLambdaOrdinal < lambdas.size()) { - PsiElement firstElem = DebuggerUtilsEx.getFirstElementOnTheLine(lambdas.get(myLambdaOrdinal), document, line); - if (firstElem != null) { - return SourcePosition.createFromElement(firstElem); - } + Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file); + if (document == null || line >= document.getLineCount()) { + return original; + } + if (myLambdaOrdinal < lambdas.size()) { + PsiElement firstElem = DebuggerUtilsEx.getFirstElementOnTheLine(lambdas.get(myLambdaOrdinal), document, line); + if (firstElem != null) { + return SourcePosition.createFromElement(firstElem); } } - else { - // There may be more than one class/method code on the line, so we need to find out the correct place - for (PsiElement elem : getLineElements(file, line)) { - PsiElement remappedElement = remapElement(elem); - if (remappedElement != null) { - if (remappedElement.getTextOffset() <= original.getOffset()) break; - return SourcePosition.createFromElement(remappedElement); - } - } - } - return original; } + else { + // There may be more than one class/method code on the line, so we need to find out the correct place + for (PsiElement elem : getLineElements(file, line)) { + PsiElement remappedElement = remapElement(elem); + if (remappedElement != null) { + if (remappedElement.getTextOffset() <= original.getOffset()) break; + return SourcePosition.createFromElement(remappedElement); + } + } + } + return original; }); } } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/RemappedSourcePosition.java b/java/debugger/impl/src/com/intellij/debugger/engine/RemappedSourcePosition.java index 8fb462314bd9..51d2bd00017e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/RemappedSourcePosition.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/RemappedSourcePosition.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ abstract class RemappedSourcePosition extends SourcePosition { private SourcePosition myDelegate; private boolean myMapped = false; - public RemappedSourcePosition(SourcePosition delegate) { + public RemappedSourcePosition(@NotNull SourcePosition delegate) { myDelegate = delegate; } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java index 63e3db1c90f1..e7c692b80401 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java @@ -761,6 +761,20 @@ public abstract class DebuggerUtilsEx extends DebuggerUtils { return null; } + @Nullable + public static SourcePosition toSourcePosition(@Nullable XSourcePosition position, Project project) { + if (position != null) { + if (position instanceof JavaXSourcePosition) { + return ((JavaXSourcePosition)position).mySourcePosition; + } + PsiFile psiFile = getPsiFile(position, project); + if (psiFile != null) { + return SourcePosition.createFromLine(psiFile, position.getLine()); + } + } + return null; + } + private static class JavaXSourcePosition implements XSourcePosition, ExecutionPointHighlighter.HighlighterProvider { private final SourcePosition mySourcePosition; @NotNull private final VirtualFile myFile; @@ -799,6 +813,18 @@ public abstract class DebuggerUtilsEx extends DebuggerUtils { } } + @Nullable + public static PsiFile getPsiFile(@Nullable XSourcePosition position, Project project) { + ApplicationManager.getApplication().assertReadAccessAllowed(); + if (position != null) { + VirtualFile file = position.getFile(); + if (file.isValid()) { + return PsiManager.getInstance(project).findFile(file); + } + } + return null; + } + /** * Decompiler aware version */ diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointWithHighlighter.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointWithHighlighter.java index 2e4ec9617827..8ccee6bf428a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointWithHighlighter.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointWithHighlighter.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import com.intellij.debugger.engine.JVMNameUtil; import com.intellij.debugger.engine.events.DebuggerCommandImpl; import com.intellij.debugger.engine.requests.RequestManagerImpl; import com.intellij.debugger.impl.DebuggerContextImpl; +import com.intellij.debugger.impl.DebuggerUtilsEx; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; @@ -35,7 +36,6 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; import com.intellij.psi.jsp.JspFile; import com.intellij.ui.classFilter.ClassFilter; import com.intellij.util.StringBuilderSpinAllocator; @@ -235,28 +235,10 @@ public abstract class BreakpointWithHighlighter

return true; } - @Nullable + @NotNull @Override public final XBreakpointCustomPropertiesPanel> createCustomRightPropertiesPanel(@NotNull Project project) { return new JavaBreakpointFiltersPanel<>(project); } - @Nullable + @NotNull @Override public final XDebuggerEditorsProvider getEditorsProvider(@NotNull XBreakpoint breakpoint, @NotNull Project project) { return new JavaDebuggerEditorsProvider(); @@ -64,14 +63,9 @@ public abstract class JavaBreakpointTypeBase public XSourcePosition getSourcePosition(@NotNull XBreakpoint breakpoint) { Breakpoint javaBreakpoint = BreakpointManager.getJavaBreakpoint(breakpoint); if (javaBreakpoint != null) { - final PsiClass aClass = javaBreakpoint.getPsiClass(); + PsiClass aClass = javaBreakpoint.getPsiClass(); if (aClass != null) { - return ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public XSourcePosition compute() { - return XDebuggerUtil.getInstance().createPositionByElement(aClass); - } - }); + return ReadAction.compute(() -> XDebuggerUtil.getInstance().createPositionByElement(aClass)); } } return null; diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaLineBreakpointType.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaLineBreakpointType.java index 0acacde8c0f2..2842845cbca7 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaLineBreakpointType.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaLineBreakpointType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package com.intellij.debugger.ui.breakpoints; import com.intellij.debugger.DebuggerBundle; import com.intellij.debugger.HelpID; import com.intellij.debugger.SourcePosition; +import com.intellij.debugger.engine.PositionManagerImpl; import com.intellij.debugger.impl.DebuggerUtilsEx; import com.intellij.icons.AllIcons; import com.intellij.openapi.editor.Document; @@ -33,6 +34,7 @@ import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.breakpoints.XLineBreakpoint; import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule; import com.intellij.xdebugger.impl.XSourcePositionImpl; +import com.intellij.xdebugger.impl.breakpoints.XLineBreakpointImpl; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -99,12 +101,11 @@ public class JavaLineBreakpointType extends JavaLineBreakpointTypeBase computeVariants(@NotNull Project project, @NotNull XSourcePosition position) { - PsiFile file = PsiManager.getInstance(project).findFile(position.getFile()); - if (file == null) { + SourcePosition pos = DebuggerUtilsEx.toSourcePosition(position, project); + if (pos == null) { return Collections.emptyList(); } - SourcePosition pos = SourcePosition.createFromLine(file, position.getLine()); List lambdas = DebuggerUtilsEx.collectLambdas(pos, true); if (lambdas.isEmpty()) { return Collections.emptyList(); @@ -116,7 +117,7 @@ public class JavaLineBreakpointType extends JavaLineBreakpointTypeBase breakpoint) { - JavaLineBreakpointProperties properties = breakpoint.getProperties(); - if (properties != null) { - Integer ordinal = properties.getLambdaOrdinal(); - if (ordinal != null) { - Breakpoint javaBreakpoint = BreakpointManager.getJavaBreakpoint(breakpoint); - if (javaBreakpoint instanceof LineBreakpoint) { - PsiElement method = getContainingMethod((LineBreakpoint)javaBreakpoint); - if (method != null) { - return method.getTextRange(); - } + Integer ordinal = getLambdaOrdinal(breakpoint); + if (ordinal != null) { + Breakpoint javaBreakpoint = BreakpointManager.getJavaBreakpoint(breakpoint); + if (javaBreakpoint instanceof LineBreakpoint) { + PsiElement method = getContainingMethod((LineBreakpoint)javaBreakpoint); + if (method != null) { + return method.getTextRange(); } } } return null; } + @Override + public XSourcePosition getSourcePosition(@NotNull XBreakpoint breakpoint) { + Integer ordinal = getLambdaOrdinal(breakpoint); + if (ordinal != null && ordinal > -1) { + SourcePosition linePosition = createLineSourcePosition((XLineBreakpointImpl)breakpoint); + if (linePosition != null) { + return DebuggerUtilsEx.toXSourcePosition(new PositionManagerImpl.JavaSourcePosition(linePosition, ordinal)); + } + } + return null; + } + + @Nullable + private static Integer getLambdaOrdinal(XBreakpoint breakpoint) { + JavaLineBreakpointProperties properties = breakpoint.getProperties(); + return properties != null ? properties.getLambdaOrdinal() : null; + } + + @Nullable + private static SourcePosition createLineSourcePosition(XLineBreakpointImpl breakpoint) { + VirtualFile file = breakpoint.getFile(); + if (file != null) { + PsiFile psiFile = PsiManager.getInstance(breakpoint.getProject()).findFile(file); + if (psiFile != null) { + return SourcePosition.createFromLine(psiFile, breakpoint.getLine()); + } + } + return null; + } + @Override public boolean canBeHitInOtherPlaces() { return true; // line breakpoints could be hit in other versions of the same classes diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/LineBreakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/LineBreakpoint.java index 64e558a18699..052ac43562f8 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/LineBreakpoint.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/LineBreakpoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -525,7 +525,7 @@ public class LineBreakpoint

extends Breakpoi XSourcePosition position = myXBreakpoint.getSourcePosition(); if (position != null) { int offset = position.getOffset(); - return findOwnerMethod(getPsiFile(), offset); + return findOwnerMethod(DebuggerUtilsEx.getPsiFile(myXBreakpoint.getSourcePosition(), myProject), offset); } return null; } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerTreeRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerTreeRenderer.java index d94271e38183..fb8a715d3a88 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerTreeRenderer.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerTreeRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,8 @@ import com.intellij.xdebugger.impl.ui.DebuggerUIUtil; import com.intellij.xdebugger.impl.ui.XDebugSessionTab; import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants; import com.intellij.xdebugger.impl.ui.tree.ValueMarkup; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.Value; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -74,7 +76,7 @@ public class DebuggerTreeRenderer extends ColoredTreeCellRenderer { nodeIcon = stackDescriptor.getIcon(); } else if (descriptor instanceof ValueDescriptorImpl) { - nodeIcon = getValueIcon((ValueDescriptorImpl)descriptor); + nodeIcon = getValueIcon((ValueDescriptorImpl)descriptor, null); } else if (descriptor instanceof MessageDescriptor) { MessageDescriptor messageDescriptor = (MessageDescriptor)descriptor; @@ -95,11 +97,17 @@ public class DebuggerTreeRenderer extends ColoredTreeCellRenderer { return nodeIcon; } - public static Icon getValueIcon(ValueDescriptorImpl valueDescriptor) { + public static Icon getValueIcon(ValueDescriptorImpl valueDescriptor, @Nullable ValueDescriptorImpl parentDescriptor) { Icon nodeIcon; if (valueDescriptor instanceof FieldDescriptorImpl) { FieldDescriptorImpl fieldDescriptor = (FieldDescriptorImpl)valueDescriptor; nodeIcon = PlatformIcons.FIELD_ICON; + if (parentDescriptor != null) { + Value value = valueDescriptor.getValue(); + if (value instanceof ObjectReference && value.equals(parentDescriptor.getValue())) { + nodeIcon = AllIcons.Debugger.Selfreference; + } + } if (fieldDescriptor.getField().isFinal()) { nodeIcon = new LayeredIcon(nodeIcon, AllIcons.Nodes.FinalMark); } diff --git a/java/execution/impl/src/com/intellij/execution/testframework/SearchForTestsTask.java b/java/execution/impl/src/com/intellij/execution/testframework/SearchForTestsTask.java index 5a8e1d221c8e..74a9f465a87c 100644 --- a/java/execution/impl/src/com/intellij/execution/testframework/SearchForTestsTask.java +++ b/java/execution/impl/src/com/intellij/execution/testframework/SearchForTestsTask.java @@ -52,7 +52,7 @@ public abstract class SearchForTestsTask extends Task.Backgroundable { protected abstract void search() throws ExecutionException; - protected abstract void onFound(); + protected abstract void onFound() throws ExecutionException; public void ensureFinished() { if (myProcessIndicator != null && !myProcessIndicator.isCanceled()) { @@ -68,7 +68,12 @@ public abstract class SearchForTestsTask extends Task.Backgroundable { catch (Throwable e) { LOG.error(e); } - onFound(); + try { + onFound(); + } + catch (ExecutionException e) { + LOG.error(e); + } } else { myProcessIndicator = new BackgroundableProcessIndicator(this); @@ -131,7 +136,12 @@ public abstract class SearchForTestsTask extends Task.Backgroundable { @Override public void onSuccess() { DumbService.getInstance(getProject()).runWhenSmart(() -> { - onFound(); + try { + onFound(); + } + catch (ExecutionException e) { + LOG.error(e); + } finish(); }); } diff --git a/java/execution/impl/src/com/intellij/execution/ui/JreComboboxEditor.java b/java/execution/impl/src/com/intellij/execution/ui/JreComboboxEditor.java index 4611b62acad4..5c13a5e2f9e4 100644 --- a/java/execution/impl/src/com/intellij/execution/ui/JreComboboxEditor.java +++ b/java/execution/impl/src/com/intellij/execution/ui/JreComboboxEditor.java @@ -24,6 +24,9 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.plaf.basic.BasicComboBoxEditor; +import java.awt.*; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; /** * @author nik @@ -56,6 +59,23 @@ class JreComboboxEditor extends BasicComboBoxEditor { protected JTextField createEditorComponent() { JBTextField field = new JBTextField(); field.setBorder(null); + field.addFocusListener(new FocusListener() { + @Override public void focusGained(FocusEvent e) { + update(e); + } + @Override public void focusLost(FocusEvent e) { + update(e); + } + + private void update(FocusEvent e) { + Component c = e.getComponent().getParent(); + if (c != null) { + c.revalidate(); + c.repaint(); + } + } + }); + return field; } diff --git a/java/idea-ui/src/com/intellij/jarRepository/JarRepositoryManager.java b/java/idea-ui/src/com/intellij/jarRepository/JarRepositoryManager.java index 8db950e1aedd..1ee2fe74f79e 100644 --- a/java/idea-ui/src/com/intellij/jarRepository/JarRepositoryManager.java +++ b/java/idea-ui/src/com/intellij/jarRepository/JarRepositoryManager.java @@ -48,6 +48,8 @@ import com.intellij.util.concurrency.SequentialTaskExecutor; import gnu.trove.THashMap; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.transfer.RepositoryOfflineException; +import org.eclipse.aether.transfer.TransferCancelledException; import org.eclipse.aether.version.Version; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -373,7 +375,7 @@ public class JarRepositoryManager { @Nullable private static T submitModalJob(@Nullable final Project project, final String title, final Function job){ final Ref result = Ref.create(null); - new Task.Modal(project, title, false) { // todo: is cancel support possible with aether? + new Task.Modal(project, title, true) { @Override public void run(@NotNull ProgressIndicator indicator) { try { @@ -393,7 +395,7 @@ public class JarRepositoryManager { try { ourTasksInProgress++; final ProgressIndicator indicator = new EmptyProgressIndicator(); - return ProgressManager.getInstance().runProcess(() -> job.apply(indicator), indicator); + return ProgressManager.getInstance().runProcess(() -> job.apply(indicator), indicator); } catch (ProcessCanceledException ignored){ } @@ -408,10 +410,15 @@ public class JarRepositoryManager { } private static Collection lookupVersionsImpl(final String groupId, final String artifactId, ArtifactRepositoryManager manager) throws Exception { - final List result = manager.getAvailableVersions(groupId, artifactId, "[0,)", ArtifactKind.ARTIFACT); - return result.stream().sorted(Comparator.reverseOrder()).map(Version::toString).collect( - Collectors.toCollection(() -> new ArrayList<>(result.size())) - ); + try { + final List result = manager.getAvailableVersions(groupId, artifactId, "[0,)", ArtifactKind.ARTIFACT); + return result.stream().sorted(Comparator.reverseOrder()).map(Version::toString).collect( + Collectors.toCollection(() -> new ArrayList<>(result.size())) + ); + } + catch (TransferCancelledException e) { + throw new ProcessCanceledException(e); + } } private static abstract class AetherJob implements Function { @@ -430,6 +437,7 @@ public class JarRepositoryManager { public final T apply(ProgressIndicator indicator) { if (canStart()) { indicator.setText(getProgressText()); + indicator.setIndeterminate(true); final ArrayList remotes = new ArrayList<>(); for (RemoteRepositoryDescription repository : myRepositories) { @@ -441,8 +449,16 @@ public class JarRepositoryManager { public void consume(String message) { indicator.setText(message); } + + @Override + public boolean isCanceled() { + return indicator.isCanceled(); + } })); } + catch (ProcessCanceledException e) { + throw e; + } catch (Exception e) { LOG.info(e); } @@ -536,12 +552,23 @@ public class JarRepositoryManager { try { return manager.resolveDependencyAsArtifact(myDesc.getGroupId(), myDesc.getArtifactId(), version, myKinds); } + catch (TransferCancelledException e) { + throw new ProcessCanceledException(e); + } + catch (RepositoryOfflineException e) { + throw e; + } catch (Exception e) { final String resolvedVersion = resolveVersion(manager, version); if (Comparing.equal(version, resolvedVersion)) { // no changes throw e; } - return manager.resolveDependencyAsArtifact(myDesc.getGroupId(), myDesc.getArtifactId(), resolvedVersion, myKinds); + try { + return manager.resolveDependencyAsArtifact(myDesc.getGroupId(), myDesc.getArtifactId(), resolvedVersion, myKinds); + } + catch (TransferCancelledException e1) { + throw new ProcessCanceledException(e1); + } } } diff --git a/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileContainerImpl.java b/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileContainerImpl.java index 1521876d9f1f..65940142a889 100644 --- a/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileContainerImpl.java +++ b/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileContainerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,7 +47,7 @@ public class ConfigFileContainerImpl extends SimpleModificationTracker implement myConfiguration = configuration; myMetaDataProvider = descriptorMetaDataProvider; myProject = project; - VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() { + VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileListener() { @Override public void propertyChanged(@NotNull final VirtualFilePropertyEvent event) { if (event.getPropertyName().equals(VirtualFile.PROP_NAME)) { @@ -128,7 +128,7 @@ public class ConfigFileContainerImpl extends SimpleModificationTracker implement } public void updateDescriptors(@NotNull MultiValuesMap descriptorsMap) { - Set toDelete = myConfigFiles.isEmpty() ? Collections.emptySet() : new HashSet<>(myConfigFiles.values()); + Set toDelete = myConfigFiles.isEmpty() ? Collections.emptySet() : new HashSet<>(myConfigFiles.values()); Set added = null; for (Map.Entry> entry : descriptorsMap.entrySet()) { diff --git a/java/java-analysis-api/java-analysis-api.iml b/java/java-analysis-api/java-analysis-api.iml index e4036f35aded..84a6db05d79a 100644 --- a/java/java-analysis-api/java-analysis-api.iml +++ b/java/java-analysis-api/java-analysis-api.iml @@ -11,5 +11,6 @@ + \ No newline at end of file diff --git a/java/java-analysis-api/src/com/intellij/codeInspection/AbstractBaseUastLocalInspectionTool.java b/java/java-analysis-api/src/com/intellij/codeInspection/AbstractBaseUastLocalInspectionTool.java new file mode 100644 index 000000000000..4855eda6486c --- /dev/null +++ b/java/java-analysis-api/src/com/intellij/codeInspection/AbstractBaseUastLocalInspectionTool.java @@ -0,0 +1,115 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInspection; + +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.uast.UastVisitorAdapter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.uast.UClass; +import org.jetbrains.uast.UField; +import org.jetbrains.uast.UFile; +import org.jetbrains.uast.UMethod; +import org.jetbrains.uast.visitor.AbstractUastVisitor; + +public abstract class AbstractBaseUastLocalInspectionTool extends LocalInspectionTool { + private static final Condition PROBLEM_ELEMENT_CONDITION = Conditions.and(Conditions.instanceOf(PsiFile.class, PsiClass.class, PsiMethod.class, PsiField.class), Conditions.notInstanceOf(PsiTypeParameter.class)); + + /** + * Override this to report problems at method level. + * + * @param method to check. + * @param manager InspectionManager to ask for ProblemDescriptors from. + * @param isOnTheFly true if called during on the fly editor highlighting. Called from Inspect Code action otherwise. + * @return {@code null} if no problems found or not applicable at method level. + */ + @Nullable + public ProblemDescriptor[] checkMethod(@NotNull UMethod method, @NotNull InspectionManager manager, boolean isOnTheFly) { + return null; + } + + /** + * Override this to report problems at class level. + * + * @param aClass to check. + * @param manager InspectionManager to ask for ProblemDescriptors from. + * @param isOnTheFly true if called during on the fly editor highlighting. Called from Inspect Code action otherwise. + * @return {@code null} if no problems found or not applicable at class level. + */ + @Nullable + public ProblemDescriptor[] checkClass(@NotNull UClass aClass, @NotNull InspectionManager manager, boolean isOnTheFly) { + return null; + } + + /** + * Override this to report problems at field level. + * + * @param field to check. + * @param manager InspectionManager to ask for ProblemDescriptors from. + * @param isOnTheFly true if called during on the fly editor highlighting. Called from Inspect Code action otherwise. + * @return {@code null} if no problems found or not applicable at field level. + */ + @Nullable + public ProblemDescriptor[] checkField(@NotNull UField field, @NotNull InspectionManager manager, boolean isOnTheFly) { + return null; + } + + @Override + @NotNull + public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) { + return new UastVisitorAdapter(new AbstractUastVisitor() { + @Override + public boolean visitClass(UClass node) { + addDescriptors(checkClass(node, holder.getManager(), isOnTheFly)); + return true; + } + + @Override + public boolean visitMethod(UMethod node) { + addDescriptors(checkMethod(node, holder.getManager(), isOnTheFly)); + return true; + } + + @Override + public boolean visitField(UField node) { + addDescriptors(checkField(node, holder.getManager(), isOnTheFly)); + return true; + } + + @Override + public boolean visitFile(UFile node) { + addDescriptors(checkFile(node.getPsi(), holder.getManager(), isOnTheFly)); + return true; + } + + private void addDescriptors(final ProblemDescriptor[] descriptors) { + if (descriptors != null) { + for (ProblemDescriptor descriptor : descriptors) { + holder.registerProblem(descriptor); + } + } + } + }); + } + + @Override + public PsiNamedElement getProblemElement(final PsiElement psiElement) { + return (PsiNamedElement)PsiTreeUtil.findFirstParent(psiElement, PROBLEM_ELEMENT_CONDITION); + } +} diff --git a/java/java-analysis-api/src/com/intellij/uast/UastMetaLanguage.java b/java/java-analysis-api/src/com/intellij/uast/UastMetaLanguage.java new file mode 100644 index 000000000000..8793f5b49eaa --- /dev/null +++ b/java/java-analysis-api/src/com/intellij/uast/UastMetaLanguage.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.uast; + +import com.intellij.lang.Language; +import com.intellij.lang.MetaLanguage; +import com.intellij.util.containers.HashSet; +import org.jetbrains.uast.UastLanguagePlugin; + +import java.util.Collection; +import java.util.Set; + +/** + * @author yole + */ +public class UastMetaLanguage extends MetaLanguage { + private final Set myLanguages = new HashSet<>(); + + protected UastMetaLanguage() { + super("UAST"); + for (UastLanguagePlugin plugin: UastLanguagePlugin.Companion.getInstances()) { + myLanguages.add(plugin.getLanguage()); + } + } + + @Override + public boolean matchesLanguage(Language language) { + return myLanguages.contains(language); + } + + @Override + public Collection getMatchingLanguages() { + return myLanguages; + } +} diff --git a/java/java-analysis-api/src/com/intellij/uast/UastVisitorAdapter.java b/java/java-analysis-api/src/com/intellij/uast/UastVisitorAdapter.java new file mode 100644 index 000000000000..db9508199519 --- /dev/null +++ b/java/java-analysis-api/src/com/intellij/uast/UastVisitorAdapter.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.uast; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import org.jetbrains.uast.UElement; +import org.jetbrains.uast.UastContextKt; +import org.jetbrains.uast.visitor.UastVisitor; + +/** + * @author yole + */ +public class UastVisitorAdapter extends PsiElementVisitor { + private final UastVisitor myUastVisitor; + + public UastVisitorAdapter(UastVisitor visitor) { + myUastVisitor = visitor; + } + + @Override + public void visitElement(PsiElement element) { + super.visitElement(element); + UElement uElement = UastContextKt.toUElement(element); + if (uElement != null) { + uElement.accept(myUastVisitor); + } + } +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/miscGenerics/SuspiciousCollectionsMethodCallsInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/miscGenerics/SuspiciousCollectionsMethodCallsInspection.java index 020a710a00a8..05600652f56c 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/miscGenerics/SuspiciousCollectionsMethodCallsInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/miscGenerics/SuspiciousCollectionsMethodCallsInspection.java @@ -104,7 +104,7 @@ public class SuspiciousCollectionsMethodCallsInspection extends BaseJavaBatchLoc final boolean reportConvertibleMethodCalls, final List patternMethods, final IntArrayList indices) { final PsiExpression[] args = methodCall.getArgumentList().getExpressions(); - if (args.length != 1) return null; + if (args.length < 1) return null; PsiType argType = args[0].getType(); boolean exactType = args[0] instanceof PsiNewExpression; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/miscGenerics/SuspiciousMethodCallUtil.java b/java/java-analysis-impl/src/com/intellij/codeInspection/miscGenerics/SuspiciousMethodCallUtil.java index c8d63ea2a492..ca33fec44610 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/miscGenerics/SuspiciousMethodCallUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/miscGenerics/SuspiciousMethodCallUtil.java @@ -68,12 +68,27 @@ public class SuspiciousMethodCallUtil { if (mapClass != null) { PsiMethod remove = MethodSignatureUtil.findMethodBySignature(mapClass, removeSignature, false); addMethod(remove, 0, patternMethods, indices); + MethodSignature getSignature = MethodSignatureUtil.createMethodSignature("get", javaLangObject, PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY); PsiMethod get = MethodSignatureUtil.findMethodBySignature(mapClass, getSignature, false); addMethod(get, 0, patternMethods, indices); + + MethodSignature getOrDefaultSignature = MethodSignatureUtil.createMethodSignature("getOrDefault", + new PsiType[] {PsiType.getJavaLangObject(manager, searchScope), + PsiSubstitutor.EMPTY.substitute(mapClass.getTypeParameters()[1])}, PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY); + PsiMethod getOrDefault = MethodSignatureUtil.findMethodBySignature(mapClass, getOrDefaultSignature, false); + addMethod(getOrDefault, 0, patternMethods, indices); + + MethodSignature removeWithDefaultSignature = MethodSignatureUtil.createMethodSignature("remove", + new PsiType[] {PsiType.getJavaLangObject(manager, searchScope), PsiType.getJavaLangObject(manager, searchScope)}, + PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY); + PsiMethod removeWithDefault = MethodSignatureUtil.findMethodBySignature(mapClass, removeWithDefaultSignature, false); + addMethod(removeWithDefault, 0, patternMethods, indices); + MethodSignature containsKeySignature = MethodSignatureUtil.createMethodSignature("containsKey", javaLangObject, PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY); PsiMethod containsKey = MethodSignatureUtil.findMethodBySignature(mapClass, containsKeySignature, false); addMethod(containsKey, 0, patternMethods, indices); + MethodSignature containsValueSignature = MethodSignatureUtil.createMethodSignature("containsValue", javaLangObject, PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY); PsiMethod containsValue = MethodSignatureUtil.findMethodBySignature(mapClass, containsValueSignature, false); addMethod(containsValue, 1, patternMethods, indices); diff --git a/java/java-analysis-impl/src/com/intellij/uast/UastElementAnchorProvider.java b/java/java-analysis-impl/src/com/intellij/uast/UastElementAnchorProvider.java new file mode 100644 index 000000000000..3873f35ad98a --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/uast/UastElementAnchorProvider.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.uast; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.impl.smartPointers.SmartPointerAnchorProvider; +import com.intellij.reference.SoftReference; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.uast.UElement; +import org.jetbrains.uast.UastContextKt; + +/** + * @author yole + */ +public class UastElementAnchorProvider extends SmartPointerAnchorProvider { + @Nullable + @Override + public PsiElement getAnchor(@NotNull PsiElement element) { + if (element instanceof UElement) { + PsiElement psi = ((UElement)element).getPsi(); + if (psi != null) { + psi.putUserData(UastContextKt.getCACHED_UELEMENT_KEY(), new SoftReference<>((UElement)element)); + } + return psi; + } + return null; + } + + @Nullable + @Override + public PsiElement restoreElement(@NotNull PsiElement anchor) { + return (PsiElement)UastContextKt.toUElement(anchor); + } +} diff --git a/java/java-impl/java-impl.iml b/java/java-impl/java-impl.iml index fa544ff29c8e..cc64822a065b 100644 --- a/java/java-impl/java-impl.iml +++ b/java/java-impl/java-impl.iml @@ -49,6 +49,7 @@ + diff --git a/java/java-impl/src/com/intellij/codeInsight/ExternalAnnotationsManagerImpl.java b/java/java-impl/src/com/intellij/codeInsight/ExternalAnnotationsManagerImpl.java index c31dc89805ce..9e80970f4c93 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ExternalAnnotationsManagerImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/ExternalAnnotationsManagerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -763,7 +763,7 @@ public class ExternalAnnotationsManagerImpl extends ReadableExternalAnnotationsM } } - private class MyVirtualFileListener extends VirtualFileAdapter { + private class MyVirtualFileListener implements VirtualFileListener { private void processEvent(VirtualFileEvent event) { if (event.isFromRefresh() && ANNOTATIONS_XML.equals(event.getFileName())) { dropCache(); diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/FunctionalExpressionCompletionProvider.java b/java/java-impl/src/com/intellij/codeInsight/completion/FunctionalExpressionCompletionProvider.java index 751903a5c8a2..46aa5a4327e6 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/FunctionalExpressionCompletionProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/FunctionalExpressionCompletionProvider.java @@ -69,10 +69,10 @@ public class FunctionalExpressionCompletionProvider extends CompletionProvider result) { if (!PsiUtil.isLanguageLevel8OrHigher(parameters.getOriginalFile()) || !isLambdaContext(parameters.getPosition())) return; ExpectedTypeInfo[] expectedTypes = JavaSmartCompletionContributor.getExpectedTypes(parameters); @@ -109,12 +109,12 @@ public class FunctionalExpressionCompletionProvider extends CompletionProvider result.addElement(smart ? JavaSmartCompletionContributor.decorate(element, Arrays.asList(expectedTypes)) : element)); + smart, addInheritors, parameters, matcher, functionalInterfaceType, functionalInterfaceMethod, params, originalPosition, substitutor, + element -> result.consume(smart ? JavaSmartCompletionContributor.decorate(element, Arrays.asList(expectedTypes)) : element)); } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java index 25152c12392a..c5923c83fa44 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java @@ -45,6 +45,7 @@ import com.intellij.psi.filters.classes.AnnotationTypeFilter; import com.intellij.psi.filters.classes.AssignableFromContextFilter; import com.intellij.psi.filters.element.ModifierFilter; import com.intellij.psi.filters.getters.ExpectedTypesGetter; +import com.intellij.psi.filters.getters.JavaMembersGetter; import com.intellij.psi.impl.source.PsiJavaCodeReferenceElementImpl; import com.intellij.psi.impl.source.PsiLabelReference; import com.intellij.psi.impl.source.tree.ElementType; @@ -53,9 +54,11 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.PsiUtilCore; import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.util.Consumer; import com.intellij.util.DocumentUtil; import com.intellij.util.ProcessingContext; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -213,7 +216,7 @@ public class JavaCompletionContributor extends CompletionContributor { JavaCompletionSession session = new JavaCompletionSession(result); if (ANNOTATION_ATTRIBUTE_NAME.accepts(position) && !JavaKeywordCompletion.isAfterPrimitiveOrArrayType(position)) { - JavaKeywordCompletion.addExpectedTypeMembers(parameters, result); + addExpectedTypeMembers(parameters, result); JavaKeywordCompletion.addPrimitiveTypes(result, position, session); completeAnnotationAttributeName(result, position, parameters); result.stopHere(); @@ -229,15 +232,21 @@ public class JavaCompletionContributor extends CompletionContributor { return; } - if (position instanceof PsiIdentifier) { - addIdentifierVariants(parameters, position, result, matcher, parent, session); - } - - if (JavaMemberNameCompletionContributor.INSIDE_TYPE_PARAMS_PATTERN.accepts(position)) { + if (JavaKeywordCompletion.addWildcardExtendsSuper(result, position)) { return; } - Set usedWords = addReferenceVariants(parameters, result, session); + if (position instanceof PsiIdentifier) { + addIdentifierVariants(parameters, position, result, session, matcher); + } + + MultiMap referenceVariants = addReferenceVariants(parameters, result, session); + Set usedWords = ContainerUtil.map2Set(referenceVariants.values(), LookupElement::getLookupString); + for (Map.Entry> entry : referenceVariants.entrySet()) { + session.registerBatchItems(entry.getKey(), entry.getValue()); + } + + session.flushBatchItems(); if (psiElement().inside(PsiLiteralExpression.class).accepts(position)) { PsiReference reference = position.getContainingFile().findReferenceAt(parameters.getOffset()); @@ -253,7 +262,7 @@ public class JavaCompletionContributor extends CompletionContributor { addAllClasses(parameters, result, session); if (position instanceof PsiIdentifier) { - FunctionalExpressionCompletionProvider.addFunctionalVariants(parameters, false, true, result); + FunctionalExpressionCompletionProvider.addFunctionalVariants(parameters, false, true, result.getPrefixMatcher(), result); } if (position instanceof PsiIdentifier && @@ -270,46 +279,62 @@ public class JavaCompletionContributor extends CompletionContributor { private static void addIdentifierVariants(@NotNull CompletionParameters parameters, PsiElement position, CompletionResultSet result, - PrefixMatcher matcher, - PsiElement parent, - @NotNull JavaCompletionSession session) { - if (TypeArgumentCompletionProvider.IN_TYPE_ARGS.accepts(position)) { - new TypeArgumentCompletionProvider(false, session).addCompletions(parameters, new ProcessingContext(), result); - } - - FunctionalExpressionCompletionProvider.addFunctionalVariants(parameters, false, false, result); + JavaCompletionSession session, PrefixMatcher matcher) { + session.registerBatchItems(result, getFastIdentifierVariants(parameters, position, matcher, position.getParent(), session)); if (JavaSmartCompletionContributor.AFTER_NEW.accepts(position)) { - new JavaInheritorsGetter(ConstructorInsertHandler.BASIC_INSTANCE).generateVariants(parameters, matcher, session); + session.flushBatchItems(); + new JavaInheritorsGetter(ConstructorInsertHandler.BASIC_INSTANCE).generateVariants(parameters, matcher, session::addClassItem); } + suggestSmartCast(parameters, session, false, result); + } + + private static void suggestSmartCast(CompletionParameters parameters, JavaCompletionSession session, boolean quick, Consumer result) { + if (SmartCastProvider.shouldSuggestCast(parameters)) { + session.flushBatchItems(); + SmartCastProvider.addCastVariants(parameters, session.getMatcher(), element -> { + registerClassFromTypeElement(element, session); + result.consume(PrioritizedLookupElement.withPriority(element, 1)); + }, quick); + } + } + + private static List getFastIdentifierVariants(@NotNull CompletionParameters parameters, + PsiElement position, + PrefixMatcher matcher, + PsiElement parent, + @NotNull JavaCompletionSession session) { + List items = new ArrayList<>(); + if (TypeArgumentCompletionProvider.IN_TYPE_ARGS.accepts(position)) { + new TypeArgumentCompletionProvider(false, session).addTypeArgumentVariants(parameters, items::add, matcher); + } + + FunctionalExpressionCompletionProvider.addFunctionalVariants(parameters, false, false, matcher, items::add); + if (MethodReturnTypeProvider.IN_METHOD_RETURN_TYPE.accepts(position)) { MethodReturnTypeProvider.addProbableReturnTypes(parameters, element -> { registerClassFromTypeElement(element, session); - result.addElement(element); + items.add(element); }); } - if (SmartCastProvider.shouldSuggestCast(parameters)) { - SmartCastProvider.addCastVariants(parameters, result.getPrefixMatcher(), element -> { - registerClassFromTypeElement(element, session); - result.addElement(PrioritizedLookupElement.withPriority(element, 1)); - }); - } + suggestSmartCast(parameters, session, true, items::add); if (parent instanceof PsiReferenceExpression) { final List expected = Arrays.asList(ExpectedTypesProvider.getExpectedTypes((PsiExpression)parent, true)); CollectConversion.addCollectConversion((PsiReferenceExpression)parent, expected, - JavaSmartCompletionContributor.decorateWithoutTypeCheck(result, expected)); + lookupElement -> items.add(JavaSmartCompletionContributor.decorate(lookupElement, expected))); } if (IMPORT_REFERENCE.accepts(position)) { - result.addElement(LookupElementBuilder.create("*")); + items.add(LookupElementBuilder.create("*")); } - addKeywords(parameters, result, session); + items.addAll(new JavaKeywordCompletion(parameters, session).getResults()); - addExpressionVariants(parameters, position, result); + addExpressionVariants(parameters, position, items::add); + return items; } private static void registerClassFromTypeElement(LookupElement element, JavaCompletionSession session) { @@ -326,12 +351,12 @@ public class JavaCompletionContributor extends CompletionContributor { } } - private static void addExpressionVariants(@NotNull CompletionParameters parameters, PsiElement position, CompletionResultSet result) { + private static void addExpressionVariants(@NotNull CompletionParameters parameters, PsiElement position, Consumer result) { if (JavaSmartCompletionContributor.INSIDE_EXPRESSION.accepts(position) && !JavaKeywordCompletion.AFTER_DOT.accepts(position) && !SmartCastProvider.shouldSuggestCast(parameters)) { - JavaKeywordCompletion.addExpectedTypeMembers(parameters, result); + addExpectedTypeMembers(parameters, result); if (SameSignatureCallParametersProvider.IN_CALL_ARGUMENT.accepts(position)) { - new SameSignatureCallParametersProvider().addCompletions(parameters, new ProcessingContext(), result); + new SameSignatureCallParametersProvider().addSignatureItems(parameters, result); } } } @@ -364,8 +389,8 @@ public class JavaCompletionContributor extends CompletionContributor { } } - private static Set addReferenceVariants(final CompletionParameters parameters, CompletionResultSet result, JavaCompletionSession session) { - final Set usedWords = new HashSet<>(); + private static MultiMap addReferenceVariants(final CompletionParameters parameters, CompletionResultSet result, JavaCompletionSession session) { + MultiMap items = MultiMap.create(); final PsiElement position = parameters.getPosition(); final boolean first = parameters.getInvocationCount() <= 1; final boolean isSwitchLabel = SWITCH_LABEL.accepts(position); @@ -396,7 +421,7 @@ public class JavaCompletionContributor extends CompletionContributor { } if (isSwitchLabel) { - result1.addElement(new IndentingDecorator(TailTypeDecorator.withTail(element, TailType.createSimpleTailType(':')))); + items.putValue(result1, new IndentingDecorator(TailTypeDecorator.withTail(element, TailType.createSimpleTailType(':')))); } else { final LookupItem item = element.as(LookupItem.CLASS_CONDITION_KEY); @@ -418,14 +443,14 @@ public class JavaCompletionContributor extends CompletionContributor { } } - result1.addElement(element); + items.putValue(result1, element); } } } return; } if (reference instanceof PsiLabelReference) { - LabelReferenceCompletion.processLabelReference(result1, (PsiLabelReference)reference); + items.putValues(result1, LabelReferenceCompletion.processLabelReference((PsiLabelReference)reference)); return; } @@ -439,40 +464,25 @@ public class JavaCompletionContributor extends CompletionContributor { LOG.error("Position=" + position + "\n;Reference=" + reference + "\n;variants=" + Arrays.toString(variants)); } if (completion instanceof LookupElement && !session.alreadyProcessed((LookupElement)completion)) { - usedWords.add(((LookupElement)completion).getLookupString()); - result1.addElement((LookupElement)completion); + items.putValue(result1, (LookupElement)completion); } else if (completion instanceof PsiClass) { Condition condition = psiClass -> !session.alreadyProcessed(psiClass) && JavaCompletionUtil.isSourceLevelAccessible(position, psiClass, pkgContext); - for (JavaPsiClassReferenceElement item : JavaClassNameCompletionContributor.createClassLookupItems((PsiClass)completion, - isAfterNew, - JavaClassNameInsertHandler.JAVA_CLASS_INSERT_HANDLER, - condition)) { - usedWords.add(item.getLookupString()); - result1.addElement(item); - } + items.putValues(result1, JavaClassNameCompletionContributor.createClassLookupItems( + (PsiClass)completion, + isAfterNew, + JavaClassNameInsertHandler.JAVA_CLASS_INSERT_HANDLER, + condition)); } else { //noinspection deprecation - LookupElement element = LookupItemUtil.objectToLookupItem(completion); - usedWords.add(element.getLookupString()); - result1.addElement(element); + items.putValue(result1, LookupItemUtil.objectToLookupItem(completion)); } } }); - return usedWords; - } - - private static void addKeywords(CompletionParameters parameters, CompletionResultSet result, JavaCompletionSession session) { - JavaKeywordCompletion.addKeywords(parameters, session, element -> { - if (element.getLookupString().startsWith(result.getPrefixMatcher().getPrefix())) { - result.addElement(element); - } - }); - - JavaKeywordCompletion.addEnumCases(result, parameters.getPosition()); + return items; } static boolean isClassNamePossible(CompletionParameters parameters) { @@ -859,6 +869,14 @@ public class JavaCompletionContributor extends CompletionContributor { return null; } + private static void addExpectedTypeMembers(CompletionParameters parameters, final Consumer result) { + if (parameters.getInvocationCount() <= 1) { // on second completion, StaticMemberProcessor will suggest those + for (final ExpectedTypeInfo info : JavaSmartCompletionContributor.getExpectedTypes(parameters)) { + new JavaMembersGetter(info.getDefaultType(), parameters).addMembers(false, result); + } + } + } + static class IndentingDecorator extends LookupElementDecorator { public IndentingDecorator(LookupElement delegate) { super(delegate); diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSession.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSession.java index f536f8906fff..c7f93c5b7f1e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSession.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSession.java @@ -19,28 +19,41 @@ import com.intellij.codeInsight.lookup.AutoCompletionPolicy; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiMethod; -import com.intellij.util.Consumer; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; import java.util.HashSet; +import java.util.Map; import java.util.Set; /** * @author peter */ -public class JavaCompletionSession implements Consumer { +public class JavaCompletionSession { private final Set myAddedClasses = new HashSet<>(); private Set myKeywords = new HashSet<>(); + private final MultiMap myBatchItems = MultiMap.create(); private final CompletionResultSet myResult; public JavaCompletionSession(CompletionResultSet result) { myResult = result; } - @Override - public void consume(LookupElement lookupElement) { + void registerBatchItems(CompletionResultSet result, Collection elements) { + myBatchItems.putValues(result, elements); + } + + void flushBatchItems() { + for (Map.Entry> entry : myBatchItems.entrySet()) { + entry.getKey().addAllElements(entry.getValue()); + } + myBatchItems.clear(); + } + + public void addClassItem(LookupElement lookupElement) { PsiClass psiClass = extractClass(lookupElement); if (psiClass != null) { registerClass(psiClass); @@ -48,6 +61,10 @@ public class JavaCompletionSession implements Consumer { myResult.addElement(AutoCompletionPolicy.NEVER_AUTOCOMPLETE.applyPolicy(lookupElement)); } + @NotNull PrefixMatcher getMatcher() { + return myResult.getPrefixMatcher(); + } + @Nullable private static PsiClass extractClass(LookupElement lookupElement) { final Object object = lookupElement.getObject(); if (object instanceof PsiClass) { @@ -83,7 +100,7 @@ public class JavaCompletionSession implements Consumer { return myKeywords.contains(keyword); } - public void registerKeyword(@NotNull String keyword) { + void registerKeyword(@NotNull String keyword) { myKeywords.add(keyword); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaKeywordCompletion.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaKeywordCompletion.java index 2b7b37cfe94d..36eda50ff656 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaKeywordCompletion.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaKeywordCompletion.java @@ -27,7 +27,6 @@ import com.intellij.patterns.ElementPattern; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.filters.*; -import com.intellij.psi.filters.getters.JavaMembersGetter; import com.intellij.psi.filters.position.*; import com.intellij.psi.impl.source.jsp.jspJava.JspClassLevelDeclarationStatement; import com.intellij.psi.jsp.JspElementType; @@ -42,6 +41,8 @@ import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import static com.intellij.openapi.util.Conditions.notInstanceOf; @@ -60,20 +61,6 @@ public class JavaKeywordCompletion { psiElement().withTreeParent( psiElement(PsiParameterList.class).andNot(psiElement(PsiAnnotationParameterList.class))))); - public static boolean isInsideParameterList(PsiElement position) { - PsiElement prev = PsiTreeUtil.prevVisibleLeaf(position); - PsiModifierList modifierList = PsiTreeUtil.getParentOfType(prev, PsiModifierList.class); - if (modifierList != null) { - if (PsiTreeUtil.isAncestor(modifierList, position, false)) { - return false; - } - PsiElement parent = modifierList.getParent(); - return parent instanceof PsiParameterList || parent instanceof PsiParameter && parent.getParent() instanceof PsiParameterList; - } - return INSIDE_PARAMETER_LIST.accepts(position); - } - - private static final AndFilter START_OF_CODE_FRAGMENT = new AndFilter( new ScopeFilter(new AndFilter( new ClassFilter(JavaCodeFragment.class), @@ -193,6 +180,46 @@ public class JavaKeywordCompletion { ); } }; + private final CompletionParameters myParameters; + private final JavaCompletionSession mySession; + private final PsiElement myPosition; + private final String myPrefix; + private final List myResults = new ArrayList<>(); + @Nullable private PsiElement myPrevLeaf; + + JavaKeywordCompletion(CompletionParameters parameters, JavaCompletionSession session) { + myParameters = parameters; + mySession = session; + myPrefix = session.getMatcher().getPrefix(); + myPosition = parameters.getPosition(); + myPrevLeaf = PsiTreeUtil.prevVisibleLeaf(myPosition); + + addKeywords(); + addEnumCases(); + } + + private void addKeyword(LookupElement element) { + if (element.getLookupString().startsWith(myPrefix)) { + myResults.add(element); + } + } + + List getResults() { + return myResults; + } + + public static boolean isInsideParameterList(PsiElement position) { + PsiElement prev = PsiTreeUtil.prevVisibleLeaf(position); + PsiModifierList modifierList = PsiTreeUtil.getParentOfType(prev, PsiModifierList.class); + if (modifierList != null) { + if (PsiTreeUtil.isAncestor(modifierList, position, false)) { + return false; + } + PsiElement parent = modifierList.getParent(); + return parent instanceof PsiParameterList || parent instanceof PsiParameter && parent.getParent() instanceof PsiParameterList; + } + return INSIDE_PARAMETER_LIST.accepts(position); + } private static TailType getReturnTail(PsiElement position) { PsiElement scope = position; @@ -220,109 +247,106 @@ public class JavaKeywordCompletion { } } - private static void addStatementKeywords(Consumer variant, PsiElement position, @Nullable PsiElement prevLeaf) { - variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.SWITCH), TailTypes.SWITCH_LPARENTH)); - variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.WHILE), TailTypes.WHILE_LPARENTH)); - variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.DO), TailTypes.DO_LBRACE)); - variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.FOR), TailTypes.FOR_LPARENTH)); - variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.IF), TailTypes.IF_LPARENTH)); - variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.TRY), TailTypes.TRY_LBRACE)); - variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.THROW), TailType.INSERT_SPACE)); - variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.NEW), TailType.INSERT_SPACE)); - variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.SYNCHRONIZED), TailTypes.SYNCHRONIZED_LPARENTH)); + private void addStatementKeywords() { + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.SWITCH), TailTypes.SWITCH_LPARENTH)); + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.WHILE), TailTypes.WHILE_LPARENTH)); + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.DO), TailTypes.DO_LBRACE)); + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.FOR), TailTypes.FOR_LPARENTH)); + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.IF), TailTypes.IF_LPARENTH)); + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.TRY), TailTypes.TRY_LBRACE)); + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.THROW), TailType.INSERT_SPACE)); + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.NEW), TailType.INSERT_SPACE)); + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.SYNCHRONIZED), TailTypes.SYNCHRONIZED_LPARENTH)); - if (PsiUtil.getLanguageLevel(position).isAtLeast(LanguageLevel.JDK_1_4)) { - variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.ASSERT), TailType.INSERT_SPACE)); + if (PsiUtil.getLanguageLevel(myPosition).isAtLeast(LanguageLevel.JDK_1_4)) { + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.ASSERT), TailType.INSERT_SPACE)); } - TailType returnTail = getReturnTail(position); - LookupElement ret = createKeyword(position, PsiKeyword.RETURN); + TailType returnTail = getReturnTail(myPosition); + LookupElement ret = createKeyword(PsiKeyword.RETURN); if (returnTail != TailType.NONE) { ret = new OverrideableSpace(ret, returnTail); } - variant.consume(ret); + addKeyword(ret); - if (psiElement().withText(";").withSuperParent(2, PsiIfStatement.class).accepts(prevLeaf) || - psiElement().withText("}").withSuperParent(3, PsiIfStatement.class).accepts(prevLeaf)) { - variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.ELSE), TailType.HUMBLE_SPACE_BEFORE_WORD)); + if (psiElement().withText(";").withSuperParent(2, PsiIfStatement.class).accepts(myPrevLeaf) || + psiElement().withText("}").withSuperParent(3, PsiIfStatement.class).accepts(myPrevLeaf)) { + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.ELSE), TailType.HUMBLE_SPACE_BEFORE_WORD)); } - if (psiElement().withText("}").withParent(psiElement(PsiCodeBlock.class).withParent(or(psiElement(PsiTryStatement.class), psiElement(PsiCatchSection.class)))).accepts(prevLeaf)) { - variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.CATCH), TailTypes.CATCH_LPARENTH)); - variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.FINALLY), TailTypes.FINALLY_LBRACE)); + if (psiElement().withText("}").withParent(psiElement(PsiCodeBlock.class).withParent(or(psiElement(PsiTryStatement.class), psiElement(PsiCatchSection.class)))).accepts(myPrevLeaf)) { + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.CATCH), TailTypes.CATCH_LPARENTH)); + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.FINALLY), TailTypes.FINALLY_LBRACE)); } } - static void addKeywords(CompletionParameters parameters, JavaCompletionSession session, Consumer result) { - final PsiElement position = parameters.getPosition(); - if (PsiTreeUtil.getNonStrictParentOfType(position, PsiLiteralExpression.class, PsiComment.class) != null) { + void addKeywords() { + if (PsiTreeUtil.getNonStrictParentOfType(myPosition, PsiLiteralExpression.class, PsiComment.class) != null) { return; } - if (addWildcardExtendsSuper(result, position)) { - return; - } + addFinal(); - PsiElement prevLeaf = PsiTreeUtil.prevVisibleLeaf(position); - addFinal(result, position, prevLeaf); - - boolean statementPosition = isStatementPosition(position); + boolean statementPosition = isStatementPosition(myPosition); if (statementPosition) { - addCaseDefault(result, position); - if (START_SWITCH.accepts(position)) { + addCaseDefault(); + if (START_SWITCH.accepts(myPosition)) { return; } - addBreakContinue(result, position); - addStatementKeywords(result, position, prevLeaf); + addBreakContinue(); + addStatementKeywords(); } - addThisSuper(result, position); + addThisSuper(); - addExpressionKeywords(parameters, result, position, prevLeaf, statementPosition); + addExpressionKeywords(statementPosition); - addFileHeaderKeywords(result, position, prevLeaf); + addFileHeaderKeywords(); - addInstanceof(result, position); + addInstanceof(); - addClassKeywords(result, position, prevLeaf); + addClassKeywords(); - addMethodHeaderKeywords(result, position, prevLeaf); + addMethodHeaderKeywords(); - addPrimitiveTypes(result, position, session); + addPrimitiveTypes(this::addKeyword, myPosition, mySession); - addClassLiteral(result, position); + addClassLiteral(); - addUnfinishedMethodTypeParameters(position, result); + addUnfinishedMethodTypeParameters(); - addExtendsImplements(result, position, prevLeaf); + addExtendsImplements(); } - private static boolean addWildcardExtendsSuper(Consumer result, PsiElement position) { + static boolean addWildcardExtendsSuper(CompletionResultSet result, PsiElement position) { if (JavaMemberNameCompletionContributor.INSIDE_TYPE_PARAMS_PATTERN.accepts(position)) { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.EXTENDS), TailType.HUMBLE_SPACE_BEFORE_WORD)); - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.SUPER), TailType.HUMBLE_SPACE_BEFORE_WORD)); + for (String keyword : ContainerUtil.ar(PsiKeyword.EXTENDS, PsiKeyword.SUPER)) { + if (keyword.startsWith(result.getPrefixMatcher().getPrefix())) { + result.addElement(new OverrideableSpace(BasicExpressionCompletionContributor.createKeywordLookupItem(position, keyword), TailType.HUMBLE_SPACE_BEFORE_WORD)); + } + } return true; } return false; } - private static void addMethodHeaderKeywords(Consumer result, PsiElement position, @Nullable PsiElement prevLeaf) { - if (psiElement().withText(")").withParents(PsiParameterList.class, PsiMethod.class).accepts(prevLeaf)) { - assert prevLeaf != null; - if (prevLeaf.getParent().getParent() instanceof PsiAnnotationMethod) { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.DEFAULT), TailType.HUMBLE_SPACE_BEFORE_WORD)); + private void addMethodHeaderKeywords() { + if (psiElement().withText(")").withParents(PsiParameterList.class, PsiMethod.class).accepts(myPrevLeaf)) { + assert myPrevLeaf != null; + if (myPrevLeaf.getParent().getParent() instanceof PsiAnnotationMethod) { + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.DEFAULT), TailType.HUMBLE_SPACE_BEFORE_WORD)); } else { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.THROWS), TailType.HUMBLE_SPACE_BEFORE_WORD)); + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.THROWS), TailType.HUMBLE_SPACE_BEFORE_WORD)); } } } - private static void addCaseDefault(Consumer result, PsiElement position) { - if (getSwitchFromLabelPosition(position) != null) { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.CASE), TailType.INSERT_SPACE)); - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.DEFAULT), TailType.CASE_COLON)); + private void addCaseDefault() { + if (getSwitchFromLabelPosition(myPosition) != null) { + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.CASE), TailType.INSERT_SPACE)); + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.DEFAULT), TailType.CASE_COLON)); } } @@ -338,8 +362,8 @@ public class JavaKeywordCompletion { return null; } - static void addEnumCases(CompletionResultSet result, PsiElement position) { - PsiSwitchStatement switchStatement = getSwitchFromLabelPosition(position); + void addEnumCases() { + PsiSwitchStatement switchStatement = getSwitchFromLabelPosition(myPosition); PsiExpression expression = switchStatement == null ? null : switchStatement.getExpression(); PsiClass switchType = expression == null ? null : PsiUtil.resolveClassInClassTypeOnly(expression.getType()); if (switchType == null || !switchType.isEnum()) return; @@ -358,49 +382,49 @@ public class JavaKeywordCompletion { .withPresentableText(prefix) .withTailText(suffix) .withLookupString(name); - result.addElement(new JavaCompletionContributor.IndentingDecorator(caseConst)); + myResults.add(new JavaCompletionContributor.IndentingDecorator(caseConst)); } } - private static void addFinal(Consumer result, PsiElement position, @Nullable PsiElement prevLeaf) { - PsiStatement statement = PsiTreeUtil.getParentOfType(position, PsiExpressionStatement.class); + private void addFinal() { + PsiStatement statement = PsiTreeUtil.getParentOfType(myPosition, PsiExpressionStatement.class); if (statement == null) { - statement = PsiTreeUtil.getParentOfType(position, PsiDeclarationStatement.class); + statement = PsiTreeUtil.getParentOfType(myPosition, PsiDeclarationStatement.class); } - if (statement != null && statement.getTextRange().getStartOffset() == position.getTextRange().getStartOffset()) { + if (statement != null && statement.getTextRange().getStartOffset() == myPosition.getTextRange().getStartOffset()) { if (!psiElement().withSuperParent(2, PsiSwitchStatement.class).afterLeaf("{").accepts(statement)) { - PsiTryStatement tryStatement = PsiTreeUtil.getParentOfType(prevLeaf, PsiTryStatement.class); + PsiTryStatement tryStatement = PsiTreeUtil.getParentOfType(myPrevLeaf, PsiTryStatement.class); if (tryStatement == null || tryStatement.getCatchSections().length > 0 || tryStatement.getFinallyBlock() != null || tryStatement.getResourceList() != null) { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.FINAL), TailType.HUMBLE_SPACE_BEFORE_WORD)); + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.FINAL), TailType.HUMBLE_SPACE_BEFORE_WORD)); return; } } } - if ((isInsideParameterList(position) || isAtResourceVariableStart(position) || isAtCatchVariableStart(position)) && - !psiElement().afterLeaf(PsiKeyword.FINAL).accepts(position) && - !AFTER_DOT.accepts(position)) { - result.consume(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.FINAL), TailType.HUMBLE_SPACE_BEFORE_WORD)); + if ((isInsideParameterList(myPosition) || isAtResourceVariableStart(myPosition) || isAtCatchVariableStart(myPosition)) && + !psiElement().afterLeaf(PsiKeyword.FINAL).accepts(myPosition) && + !AFTER_DOT.accepts(myPosition)) { + addKeyword(TailTypeDecorator.withTail(createKeyword(PsiKeyword.FINAL), TailType.HUMBLE_SPACE_BEFORE_WORD)); } } - private static void addThisSuper(Consumer result, PsiElement position) { - if (SUPER_OR_THIS_PATTERN.accepts(position)) { - final boolean afterDot = AFTER_DOT.accepts(position); - final boolean insideQualifierClass = isInsideQualifierClass(position); - final boolean insideInheritorClass = PsiUtil.isLanguageLevel8OrHigher(position) && isInsideInheritorClass(position); + private void addThisSuper() { + if (SUPER_OR_THIS_PATTERN.accepts(myPosition)) { + final boolean afterDot = AFTER_DOT.accepts(myPosition); + final boolean insideQualifierClass = isInsideQualifierClass(); + final boolean insideInheritorClass = PsiUtil.isLanguageLevel8OrHigher(myPosition) && isInsideInheritorClass(); if (!afterDot || insideQualifierClass || insideInheritorClass) { if (!afterDot || insideQualifierClass) { - result.consume(createKeyword(position, PsiKeyword.THIS)); + addKeyword(createKeyword(PsiKeyword.THIS)); } - final LookupElement superItem = createKeyword(position, PsiKeyword.SUPER); - if (psiElement().afterLeaf(psiElement().withText("{").withSuperParent(2, psiMethod().constructor(true))).accepts(position)) { - final PsiMethod method = PsiTreeUtil.getParentOfType(position, PsiMethod.class, false, PsiClass.class); + final LookupElement superItem = createKeyword(PsiKeyword.SUPER); + if (psiElement().afterLeaf(psiElement().withText("{").withSuperParent(2, psiMethod().constructor(true))).accepts(myPosition)) { + final PsiMethod method = PsiTreeUtil.getParentOfType(myPosition, PsiMethod.class, false, PsiClass.class); assert method != null; final boolean hasParams = superConstructorHasParameters(method); - result.consume(LookupElementDecorator.withInsertHandler(superItem, new ParenthesesInsertHandler() { + addKeyword(LookupElementDecorator.withInsertHandler(superItem, new ParenthesesInsertHandler() { @Override protected boolean placeCaretInsideParentheses(InsertionContext context, LookupElement item) { return hasParams; @@ -415,56 +439,56 @@ public class JavaKeywordCompletion { return; } - result.consume(superItem); + addKeyword(superItem); } } } - private static void addExpressionKeywords(CompletionParameters parameters, Consumer result, PsiElement position, @Nullable PsiElement prevLeaf, boolean statementPosition) { - if (psiElement(JavaTokenType.DOUBLE_COLON).accepts(prevLeaf)) { - PsiMethodReferenceExpression parent = PsiTreeUtil.getParentOfType(parameters.getPosition(), PsiMethodReferenceExpression.class); + private void addExpressionKeywords(boolean statementPosition) { + if (psiElement(JavaTokenType.DOUBLE_COLON).accepts(myPrevLeaf)) { + PsiMethodReferenceExpression parent = PsiTreeUtil.getParentOfType(myPosition, PsiMethodReferenceExpression.class); TailType tail = parent != null && !LambdaHighlightingUtil.insertSemicolon(parent.getParent()) ? TailType.SEMICOLON : TailType.NONE; - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.NEW), tail)); + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.NEW), tail)); return; } - if (isExpressionPosition(position)) { - if (PsiTreeUtil.getParentOfType(position, PsiAnnotation.class) == null) { + if (isExpressionPosition(myPosition)) { + if (PsiTreeUtil.getParentOfType(myPosition, PsiAnnotation.class) == null) { if (!statementPosition) { - result.consume(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.NEW), TailType.INSERT_SPACE)); + addKeyword(TailTypeDecorator.withTail(createKeyword(PsiKeyword.NEW), TailType.INSERT_SPACE)); } - result.consume(createKeyword(position, PsiKeyword.NULL)); + addKeyword(createKeyword(PsiKeyword.NULL)); } - if (mayExpectBoolean(parameters)) { - result.consume(createKeyword(position, PsiKeyword.TRUE)); - result.consume(createKeyword(position, PsiKeyword.FALSE)); + if (mayExpectBoolean(myParameters)) { + addKeyword(createKeyword(PsiKeyword.TRUE)); + addKeyword(createKeyword(PsiKeyword.FALSE)); } } } - private static void addFileHeaderKeywords(Consumer result, PsiElement position, @Nullable PsiElement prevLeaf) { - PsiFile file = position.getContainingFile(); + private void addFileHeaderKeywords() { + PsiFile file = myPosition.getContainingFile(); if (!(file instanceof PsiExpressionCodeFragment) && !(file instanceof PsiJavaCodeReferenceCodeFragment) && !(file instanceof PsiTypeCodeFragment)) { - if (prevLeaf == null) { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.PACKAGE), TailType.HUMBLE_SPACE_BEFORE_WORD)); - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.IMPORT), TailType.HUMBLE_SPACE_BEFORE_WORD)); + if (myPrevLeaf == null) { + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.PACKAGE), TailType.HUMBLE_SPACE_BEFORE_WORD)); + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.IMPORT), TailType.HUMBLE_SPACE_BEFORE_WORD)); } - else if (END_OF_BLOCK.getValue().isAcceptable(position, position) && PsiTreeUtil.getParentOfType(position, PsiMember.class) == null) { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.IMPORT), TailType.HUMBLE_SPACE_BEFORE_WORD)); + else if (END_OF_BLOCK.getValue().isAcceptable(myPosition, myPosition) && PsiTreeUtil.getParentOfType(myPosition, PsiMember.class) == null) { + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.IMPORT), TailType.HUMBLE_SPACE_BEFORE_WORD)); } } - if (PsiUtil.isLanguageLevel5OrHigher(position) && prevLeaf != null && prevLeaf.textMatches(PsiKeyword.IMPORT)) { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.STATIC), TailType.HUMBLE_SPACE_BEFORE_WORD)); + if (PsiUtil.isLanguageLevel5OrHigher(myPosition) && myPrevLeaf != null && myPrevLeaf.textMatches(PsiKeyword.IMPORT)) { + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.STATIC), TailType.HUMBLE_SPACE_BEFORE_WORD)); } } - private static void addInstanceof(Consumer result, PsiElement position) { - if (isInstanceofPlace(position)) { - result.consume(LookupElementDecorator.withInsertHandler( - createKeyword(position, PsiKeyword.INSTANCEOF), + private void addInstanceof() { + if (isInstanceofPlace(myPosition)) { + addKeyword(LookupElementDecorator.withInsertHandler( + createKeyword(PsiKeyword.INSTANCEOF), new InsertHandler>() { @Override public void handleInsert(InsertionContext context, LookupElementDecorator item) { @@ -489,42 +513,42 @@ public class JavaKeywordCompletion { } } - private static void addClassKeywords(Consumer result, PsiElement position, @Nullable PsiElement prevLeaf) { - if (isSuitableForClass(position)) { - for (String s : ModifierChooser.getKeywords(position)) { - result.consume(new OverrideableSpace(createKeyword(position, s), TailType.HUMBLE_SPACE_BEFORE_WORD)); + private void addClassKeywords() { + if (isSuitableForClass(myPosition)) { + for (String s : ModifierChooser.getKeywords(myPosition)) { + addKeyword(new OverrideableSpace(createKeyword(s), TailType.HUMBLE_SPACE_BEFORE_WORD)); } - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.CLASS), TailType.HUMBLE_SPACE_BEFORE_WORD)); - if (PsiTreeUtil.getParentOfType(position, PsiCodeBlock.class, true, PsiMember.class) == null) { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.INTERFACE), TailType.HUMBLE_SPACE_BEFORE_WORD)); - if (PsiUtil.isLanguageLevel5OrHigher(position)) { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.ENUM), TailType.INSERT_SPACE)); + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.CLASS), TailType.HUMBLE_SPACE_BEFORE_WORD)); + if (PsiTreeUtil.getParentOfType(myPosition, PsiCodeBlock.class, true, PsiMember.class) == null) { + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.INTERFACE), TailType.HUMBLE_SPACE_BEFORE_WORD)); + if (PsiUtil.isLanguageLevel5OrHigher(myPosition)) { + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.ENUM), TailType.INSERT_SPACE)); } } } if (psiElement().withText("@").andNot(psiElement().inside(PsiParameterList.class)).andNot(psiElement().inside(psiNameValuePair())) - .accepts(prevLeaf)) { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.INTERFACE), TailType.HUMBLE_SPACE_BEFORE_WORD)); + .accepts(myPrevLeaf)) { + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.INTERFACE), TailType.HUMBLE_SPACE_BEFORE_WORD)); } } - private static void addClassLiteral(Consumer result, PsiElement position) { - if (isAfterTypeDot(position)) { - result.consume(createKeyword(position, PsiKeyword.CLASS)); + private void addClassLiteral() { + if (isAfterTypeDot(myPosition)) { + addKeyword(createKeyword(PsiKeyword.CLASS)); } } - private static void addExtendsImplements(Consumer result, PsiElement position, @Nullable PsiElement prevLeaf) { - if (prevLeaf == null || !(prevLeaf instanceof PsiIdentifier || prevLeaf.textMatches(">"))) return; + private void addExtendsImplements() { + if (myPrevLeaf == null || !(myPrevLeaf instanceof PsiIdentifier || myPrevLeaf.textMatches(">"))) return; PsiClass psiClass = null; - PsiElement prevParent = prevLeaf.getParent(); - if (prevLeaf instanceof PsiIdentifier && prevParent instanceof PsiClass) { + PsiElement prevParent = myPrevLeaf.getParent(); + if (myPrevLeaf instanceof PsiIdentifier && prevParent instanceof PsiClass) { psiClass = (PsiClass)prevParent; } else { - PsiReferenceList referenceList = PsiTreeUtil.getParentOfType(prevLeaf, PsiReferenceList.class); + PsiReferenceList referenceList = PsiTreeUtil.getParentOfType(myPrevLeaf, PsiReferenceList.class); if (referenceList != null && referenceList.getParent() instanceof PsiClass) { psiClass = (PsiClass)referenceList.getParent(); } @@ -534,9 +558,9 @@ public class JavaKeywordCompletion { } if (psiClass != null) { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.EXTENDS), TailType.HUMBLE_SPACE_BEFORE_WORD)); + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.EXTENDS), TailType.HUMBLE_SPACE_BEFORE_WORD)); if (!psiClass.isInterface()) { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.IMPLEMENTS), TailType.HUMBLE_SPACE_BEFORE_WORD)); + addKeyword(new OverrideableSpace(createKeyword(PsiKeyword.IMPLEMENTS), TailType.HUMBLE_SPACE_BEFORE_WORD)); } } } @@ -593,20 +617,12 @@ public class JavaKeywordCompletion { return END_OF_BLOCK.getValue().isAcceptable(position, position); } - static void addExpectedTypeMembers(CompletionParameters parameters, final CompletionResultSet result) { - if (parameters.getInvocationCount() <= 1) { // on second completion, StaticMemberProcessor will suggest those - for (final ExpectedTypeInfo info : JavaSmartCompletionContributor.getExpectedTypes(parameters)) { - new JavaMembersGetter(info.getDefaultType(), parameters).addMembers(false, result); - } - } - } - - private static void addUnfinishedMethodTypeParameters(PsiElement position, final Consumer result) { + private void addUnfinishedMethodTypeParameters() { final ProcessingContext context = new ProcessingContext(); if (psiElement().inside( psiElement(PsiTypeElement.class).afterLeaf( psiElement().withText(">").withParent( - psiElement(PsiTypeParameterList.class).withParent(PsiErrorElement.class).save("typeParameterList")))).accepts(position, context)) { + psiElement(PsiTypeParameterList.class).withParent(PsiErrorElement.class).save("typeParameterList")))).accepts(myPosition, context)) { final PsiTypeParameterList list = (PsiTypeParameterList)context.get("typeParameterList"); PsiElement current = list.getParent().getParent(); if (current instanceof PsiField) { @@ -614,7 +630,7 @@ public class JavaKeywordCompletion { } if (current instanceof PsiClass) { for (PsiTypeParameter typeParameter : list.getTypeParameters()) { - result.consume(new JavaPsiClassReferenceElement(typeParameter)); + addKeyword(new JavaPsiClassReferenceElement(typeParameter)); } } } @@ -672,15 +688,15 @@ public class JavaKeywordCompletion { isStatementPosition(position)) { for (String primitiveType : PRIMITIVE_TYPES) { if (!session.isKeywordAlreadyProcessed(primitiveType)) { - result.consume(createKeyword(position, primitiveType)); + result.consume(BasicExpressionCompletionContributor.createKeywordLookupItem(position, primitiveType)); } } } if (declaration) { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.VOID), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(BasicExpressionCompletionContributor.createKeywordLookupItem(position, PsiKeyword.VOID), TailType.HUMBLE_SPACE_BEFORE_WORD)); } else if (typeFragment && ((PsiTypeCodeFragment)position.getContainingFile()).isVoidValid()) { - result.consume(createKeyword(position, PsiKeyword.VOID)); + result.consume(BasicExpressionCompletionContributor.createKeywordLookupItem(position, PsiKeyword.VOID)); } } @@ -697,15 +713,15 @@ public class JavaKeywordCompletion { return psiElement().insideStarting(psiElement(PsiTypeElement.class).withParent(PsiCatchSection.class)).accepts(position); } - private static void addBreakContinue(Consumer result, PsiElement position) { - PsiLoopStatement loop = PsiTreeUtil.getParentOfType(position, PsiLoopStatement.class, true, PsiLambdaExpression.class, PsiMember.class); + private void addBreakContinue() { + PsiLoopStatement loop = PsiTreeUtil.getParentOfType(myPosition, PsiLoopStatement.class, true, PsiLambdaExpression.class, PsiMember.class); - LookupElement br = createKeyword(position, PsiKeyword.BREAK); - LookupElement cont = createKeyword(position, PsiKeyword.CONTINUE); + LookupElement br = createKeyword(PsiKeyword.BREAK); + LookupElement cont = createKeyword(PsiKeyword.CONTINUE); TailType tailType; if (psiElement().insideSequence(true, psiElement(PsiLabeledStatement.class), or(psiElement(PsiFile.class), psiElement(PsiMethod.class), - psiElement(PsiClassInitializer.class))).accepts(position)) { + psiElement(PsiClassInitializer.class))).accepts(myPosition)) { tailType = TailType.HUMBLE_SPACE_BEFORE_WORD; } else { @@ -714,16 +730,16 @@ public class JavaKeywordCompletion { br = TailTypeDecorator.withTail(br, tailType); cont = TailTypeDecorator.withTail(cont, tailType); - if (loop != null && PsiTreeUtil.isAncestor(loop.getBody(), position, false)) { - result.consume(br); - result.consume(cont); + if (loop != null && PsiTreeUtil.isAncestor(loop.getBody(), myPosition, false)) { + addKeyword(br); + addKeyword(cont); } - if (psiElement().inside(PsiSwitchStatement.class).accepts(position)) { - result.consume(br); + if (psiElement().inside(PsiSwitchStatement.class).accepts(myPosition)) { + addKeyword(br); } - for (PsiLabeledStatement labeled : psiApi().parents(position).takeWhile(notInstanceOf(PsiMember.class)).filter(PsiLabeledStatement.class)) { - result.consume(TailTypeDecorator.withTail(LookupElementBuilder.create("break " + labeled.getName()).bold(), TailType.SEMICOLON)); + for (PsiLabeledStatement labeled : psiApi().parents(myPosition).takeWhile(notInstanceOf(PsiMember.class)).filter(PsiLabeledStatement.class)) { + addKeyword(TailTypeDecorator.withTail(LookupElementBuilder.create("break " + labeled.getName()).bold(), TailType.SEMICOLON)); } } @@ -749,18 +765,18 @@ public class JavaKeywordCompletion { return false; } - protected static LookupElement createKeyword(PsiElement position, String keyword) { - return BasicExpressionCompletionContributor.createKeywordLookupItem(position, keyword); + private LookupElement createKeyword(String keyword) { + return BasicExpressionCompletionContributor.createKeywordLookupItem(myPosition, keyword); } - private static boolean isInsideQualifierClass(PsiElement position) { - if (position.getParent() instanceof PsiJavaCodeReferenceElement) { - final PsiElement qualifier = ((PsiJavaCodeReferenceElement)position.getParent()).getQualifier(); + private boolean isInsideQualifierClass() { + if (myPosition.getParent() instanceof PsiJavaCodeReferenceElement) { + final PsiElement qualifier = ((PsiJavaCodeReferenceElement)myPosition.getParent()).getQualifier(); if (qualifier instanceof PsiJavaCodeReferenceElement) { final PsiElement qualifierClass = ((PsiJavaCodeReferenceElement)qualifier).resolve(); if (qualifierClass instanceof PsiClass) { - PsiElement parent = position; - final PsiManager psiManager = position.getManager(); + PsiElement parent = myPosition; + final PsiManager psiManager = myPosition.getManager(); while ((parent = PsiTreeUtil.getParentOfType(parent, PsiClass.class, true)) != null) { if (psiManager.areElementsEquivalent(parent, qualifierClass)) { return true; @@ -772,15 +788,15 @@ public class JavaKeywordCompletion { return false; } - private static boolean isInsideInheritorClass(PsiElement position) { - if (position.getParent() instanceof PsiJavaCodeReferenceElement) { - final PsiElement qualifier = ((PsiJavaCodeReferenceElement)position.getParent()).getQualifier(); + private boolean isInsideInheritorClass() { + if (myPosition.getParent() instanceof PsiJavaCodeReferenceElement) { + final PsiElement qualifier = ((PsiJavaCodeReferenceElement)myPosition.getParent()).getQualifier(); if (qualifier instanceof PsiJavaCodeReferenceElement) { final PsiElement qualifierClass = ((PsiJavaCodeReferenceElement)qualifier).resolve(); if (qualifierClass instanceof PsiClass && ((PsiClass)qualifierClass).isInterface()) { - PsiElement parent = position; + PsiElement parent = myPosition; while ((parent = PsiTreeUtil.getParentOfType(parent, PsiClass.class, true)) != null) { - if (PsiUtil.getEnclosingStaticElement(position, (PsiClass)parent) == null && + if (PsiUtil.getEnclosingStaticElement(myPosition, (PsiClass)parent) == null && ((PsiClass)parent).isInheritor((PsiClass)qualifierClass, true)) { return true; } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaModuleCompletion.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaModuleCompletion.java index 10e13591dd02..1f22855ed2d5 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaModuleCompletion.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaModuleCompletion.java @@ -34,7 +34,7 @@ import org.jetbrains.annotations.NotNull; import java.util.function.Predicate; -import static com.intellij.codeInsight.completion.JavaKeywordCompletion.createKeyword; +import static com.intellij.codeInsight.completion.BasicExpressionCompletionContributor.createKeywordLookupItem; class JavaModuleCompletion { static boolean isModuleFile(@NotNull PsiFile file) { @@ -74,30 +74,30 @@ class JavaModuleCompletion { private static void addFileHeaderKeywords(PsiElement position, Consumer result) { PsiElement prev = PsiTreeUtil.prevVisibleLeaf(position); if (prev == null) { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.MODULE), TailType.HUMBLE_SPACE_BEFORE_WORD)); - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.OPEN), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeywordLookupItem(position, PsiKeyword.MODULE), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeywordLookupItem(position, PsiKeyword.OPEN), TailType.HUMBLE_SPACE_BEFORE_WORD)); } else if (PsiUtil.isJavaToken(prev, JavaTokenType.OPEN_KEYWORD)) { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.MODULE), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeywordLookupItem(position, PsiKeyword.MODULE), TailType.HUMBLE_SPACE_BEFORE_WORD)); } } private static void addModuleStatementKeywords(PsiElement position, Consumer result) { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.REQUIRES), TailType.HUMBLE_SPACE_BEFORE_WORD)); - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.EXPORTS), TailType.HUMBLE_SPACE_BEFORE_WORD)); - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.OPENS), TailType.HUMBLE_SPACE_BEFORE_WORD)); - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.USES), TailType.HUMBLE_SPACE_BEFORE_WORD)); - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.PROVIDES), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeywordLookupItem(position, PsiKeyword.REQUIRES), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeywordLookupItem(position, PsiKeyword.EXPORTS), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeywordLookupItem(position, PsiKeyword.OPENS), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeywordLookupItem(position, PsiKeyword.USES), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeywordLookupItem(position, PsiKeyword.PROVIDES), TailType.HUMBLE_SPACE_BEFORE_WORD)); } private static void addProvidesStatementKeywords(PsiElement position, Consumer result) { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.WITH), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeywordLookupItem(position, PsiKeyword.WITH), TailType.HUMBLE_SPACE_BEFORE_WORD)); } private static void addRequiresStatementKeywords(PsiElement context, PsiElement position, Consumer result) { if (context.getParent() instanceof PsiRequiresStatement) { - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.TRANSITIVE), TailType.HUMBLE_SPACE_BEFORE_WORD)); - result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.STATIC), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeywordLookupItem(position, PsiKeyword.TRANSITIVE), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeywordLookupItem(position, PsiKeyword.STATIC), TailType.HUMBLE_SPACE_BEFORE_WORD)); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java index c4e834d15056..d9ab1f2b1114 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java @@ -215,7 +215,7 @@ public class JavaSmartCompletionContributor extends CompletionContributor { } @NotNull - static Consumer decorateWithoutTypeCheck(final CompletionResultSet result, final Collection infos) { + private static Consumer decorateWithoutTypeCheck(final CompletionResultSet result, final Collection infos) { return lookupElement -> result.addElement(decorate(lookupElement, infos)); } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/LabelReferenceCompletion.java b/java/java-impl/src/com/intellij/codeInsight/completion/LabelReferenceCompletion.java index 497214cd5f16..b8c321c3a67f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/LabelReferenceCompletion.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/LabelReferenceCompletion.java @@ -16,6 +16,7 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.TailType; +import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.codeInsight.lookup.TailTypeDecorator; import com.intellij.patterns.ElementPattern; @@ -24,8 +25,11 @@ import com.intellij.psi.PsiKeyword; import com.intellij.psi.PsiReference; import com.intellij.psi.impl.source.PsiLabelReference; import com.intellij.util.ProcessingContext; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; +import java.util.List; + import static com.intellij.patterns.PlatformPatterns.psiElement; /** @@ -34,10 +38,8 @@ import static com.intellij.patterns.PlatformPatterns.psiElement; class LabelReferenceCompletion extends CompletionProvider { static final ElementPattern LABEL_REFERENCE = psiElement().afterLeaf(PsiKeyword.BREAK, PsiKeyword.CONTINUE); - static void processLabelReference(CompletionResultSet result, PsiLabelReference ref) { - for (String s : ref.getVariants()) { - result.addElement(TailTypeDecorator.withTail(LookupElementBuilder.create(s), TailType.SEMICOLON)); - } + static List processLabelReference(PsiLabelReference ref) { + return ContainerUtil.map(ref.getVariants(), s -> TailTypeDecorator.withTail(LookupElementBuilder.create(s), TailType.SEMICOLON)); } @Override @@ -46,7 +48,7 @@ class LabelReferenceCompletion extends CompletionProvider @NotNull CompletionResultSet result) { PsiReference ref = parameters.getPosition().getContainingFile().findReferenceAt(parameters.getOffset()); if (ref instanceof PsiLabelReference) { - processLabelReference(result, (PsiLabelReference)ref); + result.addAllElements(processLabelReference((PsiLabelReference)ref)); } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/PreferMostUsedWeigher.java b/java/java-impl/src/com/intellij/codeInsight/completion/PreferMostUsedWeigher.java index 6feb3da0efca..5ec1c85d7756 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/PreferMostUsedWeigher.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/PreferMostUsedWeigher.java @@ -18,12 +18,22 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementWeigher; import com.intellij.compiler.CompilerReferenceService; +import com.intellij.patterns.PsiMethodPattern; +import com.intellij.patterns.StandardPatterns; +import com.intellij.psi.CommonClassNames; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiMember; import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import static com.intellij.patterns.PsiJavaPatterns.psiMethod; + class PreferMostUsedWeigher extends LookupElementWeigher { + static final PsiMethodPattern OBJECT_METHOD_PATTERN = psiMethod().withName( + StandardPatterns.string().oneOf("hashCode", "equals", "finalize", "wait", "notify", "notifyAll", "getClass", "clone", "toString")). + inClass(CommonClassNames.JAVA_LANG_OBJECT); + private final CompilerReferenceService myCompilerReferenceService; private final boolean myConstructorSuggestion; @@ -44,10 +54,13 @@ class PreferMostUsedWeigher extends LookupElementWeigher { @Override public Integer weigh(@NotNull LookupElement element) { final PsiElement psi = ObjectUtils.tryCast(element.getObject(), PsiElement.class); - if (psi == null || !(psi.isPhysical())) { + if (!(psi instanceof PsiMember)) { return null; } else { + if (OBJECT_METHOD_PATTERN.accepts(psi)) { + return null; + } final Integer occurrenceCount = myCompilerReferenceService.getCompileTimeOccurrenceCount(psi, myConstructorSuggestion); return occurrenceCount == null ? null : - occurrenceCount; } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/SameSignatureCallParametersProvider.java b/java/java-impl/src/com/intellij/codeInsight/completion/SameSignatureCallParametersProvider.java index 4a78f330e5ee..810221cd0b31 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/SameSignatureCallParametersProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/SameSignatureCallParametersProvider.java @@ -28,7 +28,7 @@ import com.intellij.psi.*; import com.intellij.psi.util.PsiSuperMethodUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.ui.LayeredIcon; -import com.intellij.util.Function; +import com.intellij.util.Consumer; import com.intellij.util.PlatformIcons; import com.intellij.util.ProcessingContext; import com.intellij.util.containers.ContainerUtil; @@ -54,6 +54,10 @@ class SameSignatureCallParametersProvider extends CompletionProvider result) { final PsiCall methodCall = PsiTreeUtil.getParentOfType(parameters.getPosition(), PsiCall.class); assert methodCall != null; Set> candidates = getCallCandidates(methodCall); @@ -64,7 +68,7 @@ class SameSignatureCallParametersProvider extends CompletionProvider 1 && candidate.first.getParameterList().getParametersCount() > 1) { PsiMethod from = getMethodToTakeParametersFrom(container, candidate.first, candidate.second); if (from != null) { - result.addElement(createParametersLookupElement(from, methodCall, candidate.first)); + result.consume(createParametersLookupElement(from, methodCall, candidate.first)); } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/SmartCastProvider.java b/java/java-impl/src/com/intellij/codeInsight/completion/SmartCastProvider.java index 1348c7e51ad8..9b7b878a6146 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/SmartCastProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/SmartCastProvider.java @@ -54,10 +54,10 @@ class SmartCastProvider extends CompletionProvider { @Override protected void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result) { - addCastVariants(parameters, result.getPrefixMatcher(), result); + addCastVariants(parameters, result.getPrefixMatcher(), result, false); } - static void addCastVariants(@NotNull CompletionParameters parameters, PrefixMatcher matcher, @NotNull Consumer result) { + static void addCastVariants(@NotNull CompletionParameters parameters, PrefixMatcher matcher, @NotNull Consumer result, boolean quick) { if (!shouldSuggestCast(parameters)) return; PsiElement position = parameters.getPosition(); @@ -74,7 +74,7 @@ class SmartCastProvider extends CompletionProvider { } ExpectedTypeInfo info = getParenthesizedCastExpectationByOperandType(position); if (info != null) { - addHierarchyTypes(parameters, matcher, info, type -> result.consume(PsiTypeLookupItem.createLookupItem(type, parent))); + addHierarchyTypes(parameters, matcher, info, type -> result.consume(PsiTypeLookupItem.createLookupItem(type, parent)), quick); } return; } @@ -119,7 +119,7 @@ class SmartCastProvider extends CompletionProvider { new ExpectedTypeInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, type, TailType.NONE, null, () -> null); } - private static void addHierarchyTypes(CompletionParameters parameters, PrefixMatcher matcher, ExpectedTypeInfo info, Consumer result) { + private static void addHierarchyTypes(CompletionParameters parameters, PrefixMatcher matcher, ExpectedTypeInfo info, Consumer result, boolean quick) { PsiType infoType = info.getType(); PsiClass infoClass = PsiUtil.resolveClassInClassTypeOnly(infoType); if (info.getKind() == ExpectedTypeInfo.TYPE_OR_SUPERTYPE) { @@ -129,7 +129,7 @@ class SmartCastProvider extends CompletionProvider { } return true; }); - } else if (infoType instanceof PsiClassType) { + } else if (infoType instanceof PsiClassType && !quick) { JavaInheritorsGetter.processInheritors(parameters, Collections.singleton((PsiClassType)infoType), matcher, type -> { if (!infoType.equals(type)) { result.consume(type); diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/TypeArgumentCompletionProvider.java b/java/java-impl/src/com/intellij/codeInsight/completion/TypeArgumentCompletionProvider.java index 062211a089a6..c16835c0cb33 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/TypeArgumentCompletionProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/TypeArgumentCompletionProvider.java @@ -33,6 +33,7 @@ import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.util.Consumer; import com.intellij.util.ProcessingContext; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.JBIterable; @@ -60,20 +61,22 @@ class TypeArgumentCompletionProvider extends CompletionProvider pair = getTypeParameterInfo(context); + void addTypeArgumentVariants(CompletionParameters parameters, Consumer result, PrefixMatcher matcher) { + final Pair pair = getTypeParameterInfo(parameters.getPosition()); if (pair == null) return; PsiTypeParameterListOwner paramOwner = pair.first; - if (suggestByExpectedType(resultSet, context, paramOwner, pair.second)) return; + if (suggestByExpectedType(result, parameters.getPosition(), paramOwner, pair.second)) return; if (mySmart && paramOwner instanceof PsiClass) { - addInheritors(parameters, resultSet, (PsiClass)paramOwner, pair.second); + addInheritors(parameters, result, (PsiClass)paramOwner, pair.second, matcher); } } - private boolean suggestByExpectedType(@NotNull CompletionResultSet resultSet, + private boolean suggestByExpectedType(Consumer result, PsiElement context, PsiTypeParameterListOwner paramOwner, int index) { PsiExpression expression = PsiTreeUtil.getContextOfType(context, PsiExpression.class, true); @@ -85,7 +88,7 @@ class TypeArgumentCompletionProvider extends CompletionProvider remainingParams = JBIterable.of(paramOwner.getTypeParameters()).skip(index); List expectedArgs = getExpectedTypeArgs(context, paramOwner, remainingParams, (PsiClassType)type); - createLookupItems(resultSet, context, info, expectedArgs, paramOwner); + createLookupItems(result, context, info, expectedArgs, paramOwner); } } return true; @@ -113,21 +116,21 @@ class TypeArgumentCompletionProvider extends CompletionProvider result, PsiElement context, ExpectedTypeInfo info, List expectedArgs, PsiTypeParameterListOwner paramOwner) { if (expectedArgs.contains(null)) { PsiType arg = expectedArgs.get(0); if (arg != null) { - resultSet.addElement(TailTypeDecorator.withTail(PsiTypeLookupItem.createLookupItem(arg, context), getTail(expectedArgs.size() == 1))); + result.consume(TailTypeDecorator.withTail(PsiTypeLookupItem.createLookupItem(arg, context), getTail(expectedArgs.size() == 1))); } } else { - fillAllArgs(resultSet, context, info, expectedArgs, paramOwner); + fillAllArgs(result, context, info, expectedArgs, paramOwner); } } - private void fillAllArgs(CompletionResultSet resultSet, + private void fillAllArgs(Consumer resultSet, PsiElement context, ExpectedTypeInfo info, List expectedArgs, @@ -136,7 +139,7 @@ class TypeArgumentCompletionProvider extends CompletionProvider resultSet, + PsiClass referencedClass, + int parameterIndex, + PrefixMatcher matcher) { final List typeList = Collections.singletonList((PsiClassType)TypeConversionUtil.typeParameterErasure( referencedClass.getTypeParameters()[parameterIndex])); - JavaInheritorsGetter.processInheritors(parameters, typeList, resultSet.getPrefixMatcher(), type -> { + JavaInheritorsGetter.processInheritors(parameters, typeList, matcher, type -> { final PsiClass psiClass = PsiUtil.resolveClassInType(type); if (psiClass == null) return; - resultSet.addElement(TailTypeDecorator.withTail(new JavaPsiClassReferenceElement(psiClass), + resultSet.consume(TailTypeDecorator.withTail(new JavaPsiClassReferenceElement(psiClass), getTail(parameterIndex == referencedClass.getTypeParameters().length - 1))); }); } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/IconLineMarkerProvider.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/IconLineMarkerProvider.java index 049cc958f4a6..f96154a1ead3 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/IconLineMarkerProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/IconLineMarkerProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,23 +16,30 @@ package com.intellij.codeInsight.daemon.impl; import com.intellij.codeHighlighting.Pass; -import com.intellij.codeInsight.daemon.*; +import com.intellij.codeInsight.daemon.DaemonCodeAnalyzerSettings; +import com.intellij.codeInsight.daemon.GutterIconNavigationHandler; +import com.intellij.codeInsight.daemon.LineMarkerInfo; +import com.intellij.codeInsight.daemon.LineMarkerProviderDescriptor; import com.intellij.openapi.editor.markup.GutterIconRenderer; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.*; +import com.intellij.psi.PsiElement; import com.intellij.psi.util.ProjectIconsAccessor; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; -import com.intellij.psi.util.PsiUtilCore; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.uast.UCallExpression; +import org.jetbrains.uast.UExpression; +import org.jetbrains.uast.UastContextKt; +import org.jetbrains.uast.UastUtils; +import org.jetbrains.uast.evaluation.UEvaluationContextKt; +import org.jetbrains.uast.values.*; import javax.swing.*; import java.awt.event.MouseEvent; import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; /** * Shows small (16x16 or less) icons as gutters. @@ -50,71 +57,41 @@ public class IconLineMarkerProvider extends LineMarkerProviderDescriptor { @Override public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) { - if (element instanceof PsiAssignmentExpression) { - final PsiExpression lExpression = ((PsiAssignmentExpression)element).getLExpression(); - final PsiExpression expr = ((PsiAssignmentExpression)element).getRExpression(); - if (lExpression instanceof PsiReferenceExpression) { - PsiElement var = ((PsiReferenceExpression)lExpression).resolve(); - if (var instanceof PsiVariable) { - return createIconLineMarker(((PsiVariable)var).getType(), expr); + UCallExpression expression = UastContextKt.toUElement(element, UCallExpression.class); + if (expression == null) { + return null; + } + + if (!ProjectIconsAccessor.isIconClassType(expression.getExpressionType())) { + return null; + } + + UValue uValue = UEvaluationContextKt.uValueOf(expression); + if (uValue instanceof UCallResultValue) { + List arguments = ((UCallResultValue)uValue).getArguments(); + if (arguments.size() > 0) { + Collection constants = UValueKt.toPossibleConstants(arguments.get(0)) + .stream() + .filter(constant -> constant instanceof UStringConstant) + .map(UConstant::getSource) + .collect(Collectors.toList()); + List psiElements = UastUtils.toPsiElements(constants); + if (psiElements.size() > 0) { + return createIconLineMarker(psiElements.get(0), element); } } } - else if (element instanceof PsiReturnStatement) { - PsiReturnStatement psiReturnStatement = (PsiReturnStatement)element; - final PsiExpression value = psiReturnStatement.getReturnValue(); - final PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class); - if (method != null) { - final PsiType returnType = method.getReturnType(); - final LineMarkerInfo result = createIconLineMarker(returnType, value); - if (result != null || !ProjectIconsAccessor.isIconClassType(returnType) || value == null) return result; - - if (methodContainsReturnStatementOnly(method)) { - for (PsiReference ref : value.getReferences()) { - final PsiElement field = ref.resolve(); - if (field instanceof PsiField) { - return createIconLineMarker(returnType, ((PsiField)field).getInitializer(), psiReturnStatement); - } - } - } - } - } - else if (element instanceof PsiVariable) { - PsiVariable var = (PsiVariable)element; - - PsiUtilCore.ensureValid(var); - final PsiType type = var.getType(); - if (!type.isValid()) { - PsiUtil.ensureValidType(type, "in variable: " + var + " of " + var.getClass()); - } - - return createIconLineMarker(type, var.getInitializer()); - } return null; } - - private static boolean methodContainsReturnStatementOnly(@NotNull PsiMethod method) { - final PsiCodeBlock body = method.getBody(); - if (body == null || body.getStatements().length != 1) return false; - - return body.getStatements()[0] instanceof PsiReturnStatement; - } - @Nullable - private static LineMarkerInfo createIconLineMarker(PsiType type, @Nullable PsiExpression initializer) { - return createIconLineMarker(type, initializer, initializer); - } - - @Nullable - private static LineMarkerInfo createIconLineMarker(PsiType type, - @Nullable PsiExpression initializer, + private static LineMarkerInfo createIconLineMarker(@Nullable PsiElement initializer, PsiElement bindingElement) { if (initializer == null) return null; final Project project = initializer.getProject(); - final VirtualFile file = ProjectIconsAccessor.getInstance(project).resolveIconFile(type, initializer); + final VirtualFile file = ProjectIconsAccessor.getInstance(project).resolveIconFile(initializer); if (file == null) return null; final Icon icon = ProjectIconsAccessor.getInstance(project).getIcon(file); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaColorProvider.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaColorProvider.java index e45361bf55fb..04a086d91dd3 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaColorProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaColorProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,13 @@ import com.intellij.ui.ColorUtil; import com.intellij.ui.JBColor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.uast.*; +import org.jetbrains.uast.evaluation.UEvaluationContextKt; +import org.jetbrains.uast.values.UConstant; +import org.jetbrains.uast.values.UValue; -import java.awt.*; +import java.awt.Color; +import java.util.List; /** * @author Konstantin Bulenkov @@ -55,53 +60,54 @@ public class JavaColorProvider implements ElementColorProvider { @Nullable public static Color getJavaColorFromExpression(@Nullable PsiElement element) { - if (element instanceof PsiNewExpression) { - final PsiNewExpression expr = (PsiNewExpression)element; - if (isColorType(expr.getType())) { - return getColor(expr.getArgumentList()); - } + UCallExpression newExpression = UastContextKt.toUElement(element, UCallExpression.class); + if (newExpression != null && newExpression.getKind() == UastCallKind.CONSTRUCTOR_CALL && + isColorType(newExpression.getReturnType())) { + return getColor(newExpression.getValueArguments()); } else if (isIntLiteralInsideNewJBColorExpression(element)) { final String text = element.getText(); boolean hasAlpha = text != null && StringUtil.startsWithIgnoreCase(text, "0x") && text.length() > 8; - return new Color(getInt((PsiExpression)element), hasAlpha); + return new Color(getInt(newExpression), hasAlpha); } return null; } private static boolean isIntLiteralInsideNewJBColorExpression(PsiElement element) { - if (element instanceof PsiLiteralExpression && PsiType.INT.equals(((PsiLiteralExpression)element).getType())) { - PsiElement parent = element.getParent(); + ULiteralExpression literalExpression = UastContextKt.toUElement(element, ULiteralExpression.class); + if (literalExpression != null && PsiType.INT.equals(literalExpression.getExpressionType())) { + UElement parent = literalExpression.getUastParent(); if (parent != null) { - return isNewJBColorExpression(parent.getParent()); + return isNewJBColorExpression(parent); } } return false; } - private static boolean isNewJBColorExpression(PsiElement element) { - if (element instanceof PsiNewExpression) { - final PsiClass psiClass = PsiTypesUtil.getPsiClass(((PsiNewExpression)element).getType()); - if (psiClass != null && JBColor.class.getName().equals(psiClass.getQualifiedName())) { - return true; + private static boolean isNewJBColorExpression(UElement element) { + if (element instanceof UCallExpression) { + UCallExpression callExpression = (UCallExpression)element; + if (callExpression.getKind() == UastCallKind.CONSTRUCTOR_CALL) { + final PsiClass psiClass = PsiTypesUtil.getPsiClass(callExpression.getReturnType()); + if (psiClass != null && JBColor.class.getName().equals(psiClass.getQualifiedName())) { + return true; + } } } return false; } @Nullable - private static Color getColor(PsiExpressionList list) { + private static Color getColor(List args) { try { - final PsiExpression[] args = list.getExpressions(); - final PsiType[] types = list.getExpressionTypes(); - ColorConstructors type = getConstructorType(types); + ColorConstructors type = args.isEmpty() ? null : getConstructorType(args.size(), args.get(0).getExpressionType()); if (type != null) { switch (type) { - case INT: return new Color( getInt(args[0])); - case INT_BOOL: return new Color( getInt(args[0]), getBoolean(args[1])); - case INT_x3: return new Color( getInt(args[0]), getInt(args[1]), getInt(args[2])); - case INT_x4: return new Color( getInt(args[0]), getInt(args[1]), getInt(args[2]), getInt(args[3])); - case FLOAT_x3: return new Color(getFloat(args[0]), getFloat(args[1]), getFloat(args[2])); - case FLOAT_x4: return new Color(getFloat(args[0]), getFloat(args[1]), getFloat(args[2]), getFloat(args[3])); + case INT: return new Color( getInt(args.get(0))); + case INT_BOOL: return new Color( getInt(args.get(0)), getBoolean(args.get(1))); + case INT_x3: return new Color( getInt(args.get(0)), getInt(args.get(1)), getInt(args.get(2))); + case INT_x4: return new Color( getInt(args.get(0)), getInt(args.get(1)), getInt(args.get(2)), getInt(args.get(3))); + case FLOAT_x3: return new Color(getFloat(args.get(0)), getFloat(args.get(1)), getFloat(args.get(2))); + case FLOAT_x4: return new Color(getFloat(args.get(0)), getFloat(args.get(1)), getFloat(args.get(2)), getFloat(args.get(3))); } } } @@ -111,20 +117,25 @@ public class JavaColorProvider implements ElementColorProvider { } @Nullable - private static ColorConstructors getConstructorType(PsiType[] types) { - int len = types.length; - if (len == 0) return null; - - switch (len) { + private static ColorConstructors getConstructorType(int paramCount, PsiType paramType) { + switch (paramCount) { case 1: return ColorConstructors.INT; case 2: return ColorConstructors.INT_BOOL; - case 3: return PsiType.INT.equals(types[0]) ? ColorConstructors.INT_x3 : ColorConstructors.FLOAT_x3; - case 4: return PsiType.INT.equals(types[0]) ? ColorConstructors.INT_x4 : ColorConstructors.FLOAT_x4; + case 3: return PsiType.INT.equals(paramType) ? ColorConstructors.INT_x3 : ColorConstructors.FLOAT_x3; + case 4: return PsiType.INT.equals(paramType) ? ColorConstructors.INT_x4 : ColorConstructors.FLOAT_x4; } return null; } + public static int getInt(UExpression expr) { + return ((Integer)getObject(expr)).intValue(); + } + + public static float getFloat(UExpression expr) { + return ((Float)getObject(expr)).floatValue(); + } + public static int getInt(PsiExpression expr) { return ((Integer)getObject(expr)).intValue(); } @@ -133,7 +144,7 @@ public class JavaColorProvider implements ElementColorProvider { return ((Float)getObject(expr)).floatValue(); } - public static boolean getBoolean(PsiExpression expr) { + public static boolean getBoolean(UExpression expr) { return ((Boolean)getObject(expr)).booleanValue(); } @@ -141,6 +152,18 @@ public class JavaColorProvider implements ElementColorProvider { return JavaConstantExpressionEvaluator.computeConstantExpression(expr, true); } + private static Object getObject(UExpression expr) { + UValue value = UEvaluationContextKt.uValueOf(expr); + if (value == null) { + return null; + } + UConstant constant = value.toConstant(); + if (constant == null) { + return null; + } + return constant.getValue(); + } + @Override public void setColorTo(@NotNull PsiElement element, @NotNull Color color) { Runnable command; @@ -153,7 +176,8 @@ public class JavaColorProvider implements ElementColorProvider { assert argumentList != null; PsiExpression[] expr = argumentList.getExpressions(); - ColorConstructors type = getConstructorType(argumentList.getExpressionTypes()); + PsiType[] expressionTypes = argumentList.getExpressionTypes(); + ColorConstructors type = expressionTypes.length == 0 ? null : getConstructorType(expressionTypes.length, expressionTypes[0]); assert type != null; command = () -> { @@ -226,7 +250,7 @@ public class JavaColorProvider implements ElementColorProvider { expr.replace(factory.createExpressionFromText(String.valueOf(newValue) + "f", null)); } } - + private enum ColorConstructors { INT, INT_BOOL, INT_x3, INT_x4, FLOAT_x3, FLOAT_x4 } diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddAnnotationIntention.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddAnnotationIntention.java index 74d06b22269f..1c354e26c6f5 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddAnnotationIntention.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddAnnotationIntention.java @@ -50,7 +50,7 @@ public abstract class AddAnnotationIntention extends BaseIntentionAction { @Override public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { final PsiModifierListOwner owner = AddAnnotationPsiFix.getContainer(file, editor.getCaretModel().getOffset()); - if (owner == null || !ExternalAnnotationsManagerImpl.areExternalAnnotationsApplicable(owner)) { + if (owner == null || owner.getModifierList() == null || !ExternalAnnotationsManagerImpl.areExternalAnnotationsApplicable(owner)) { return false; } Pair annotations = getAnnotations(project); diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSubclassAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSubclassAction.java index eec03f490bfd..52fc5e91c91a 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSubclassAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSubclassAction.java @@ -26,6 +26,7 @@ package com.intellij.codeInsight.intention.impl; import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.codeInsight.CodeInsightUtil; +import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.daemon.impl.analysis.HighlightNamesUtil; import com.intellij.codeInsight.daemon.impl.quickfix.CreateClassKind; import com.intellij.codeInsight.daemon.impl.quickfix.CreateConstructorMatchingSuperFix; @@ -123,6 +124,10 @@ public class CreateSubclassAction extends BaseIntentionAction { } } + if (shouldCreateInnerClass(psiClass) && !file.getManager().isInProject(file)) { + return false; + } + myText = getTitle(psiClass); return true; } @@ -145,13 +150,18 @@ public class CreateSubclassAction extends BaseIntentionAction { final PsiClass psiClass = PsiTreeUtil.getParentOfType(element, PsiClass.class); LOG.assertTrue(psiClass != null); - if (psiClass.hasModifierProperty(PsiModifier.PRIVATE) && psiClass.getContainingClass() != null) { + if (shouldCreateInnerClass(psiClass)) { + if (!FileModificationService.getInstance().prepareFileForWrite(file)) return; createInnerClass(psiClass); return; } createTopLevelClass(psiClass); } + private boolean shouldCreateInnerClass(PsiClass psiClass) { + return psiClass.hasModifierProperty(PsiModifier.PRIVATE) && psiClass.getContainingClass() != null; + } + public static void createInnerClass(final PsiClass aClass) { new WriteCommandAction(aClass.getProject(), getTitle(aClass), getTitle(aClass)) { @Override diff --git a/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java b/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java index 35aa44340322..631b02079716 100644 --- a/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java +++ b/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java @@ -145,6 +145,7 @@ public class JavaLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSett "BRACE_STYLE", "CLASS_BRACE_STYLE", "METHOD_BRACE_STYLE", + "LAMBDA_BRACE_STYLE", "USE_FLYING_GEESE_BRACES", "FLYING_GEESE_BRACES_GAP", "DO_NOT_INDENT_TOP_LEVEL_CLASS_MEMBERS", diff --git a/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java b/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java index 89dd4aa73316..77a5e7bd00cf 100644 --- a/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java +++ b/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java @@ -67,7 +67,7 @@ public class JavaSdkImpl extends JavaSdk { public JavaSdkImpl(final VirtualFileManager fileManager, final FileTypeManager fileTypeManager) { super("JavaSDK"); - fileManager.addVirtualFileListener(new VirtualFileAdapter() { + fileManager.addVirtualFileListener(new VirtualFileListener() { @Override public void fileDeleted(@NotNull VirtualFileEvent event) { updateCache(event); diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java index 314e1f352126..45d84b017760 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java @@ -1301,12 +1301,12 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { } else if (myRole1 == ChildRole.ARROW) { if (myRole2 == ChildRole.LBRACE) { - switch (mySettings.BRACE_STYLE) { + switch (mySettings.LAMBDA_BRACE_STYLE) { case NEXT_LINE: case NEXT_LINE_SHIFTED: case NEXT_LINE_SHIFTED2: int space = spaceAroundArrow ? 1 : 0; - myResult = Spacing.createSpacing(space, space, 1, false, 0); + myResult = Spacing.createSpacing(space, space, 1, mySettings.KEEP_LINE_BREAKS, 0); break; default: createSpaceInCode(spaceAroundArrow); diff --git a/java/java-impl/src/com/intellij/psi/util/ProjectIconsAccessor.java b/java/java-impl/src/com/intellij/psi/util/ProjectIconsAccessor.java index 77d5e42a6023..301edb207123 100644 --- a/java/java-impl/src/com/intellij/psi/util/ProjectIconsAccessor.java +++ b/java/java-impl/src/com/intellij/psi/util/ProjectIconsAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,10 @@ import com.intellij.util.ui.JBUI; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.uast.UElement; +import org.jetbrains.uast.ULiteralExpression; +import org.jetbrains.uast.UastContextKt; +import org.jetbrains.uast.visitor.AbstractUastVisitor; import javax.swing.*; import java.io.File; @@ -65,23 +69,23 @@ public class ProjectIconsAccessor { } @Nullable - public VirtualFile resolveIconFile(PsiType type, @Nullable PsiExpression initializer) { - if (initializer == null || !initializer.isValid() || !isIconClassType(type)) { - return null; - } - + public VirtualFile resolveIconFile(PsiElement initializer) { final List refs = new ArrayList<>(); - initializer.accept(new JavaRecursiveElementWalkingVisitor() { + UElement initializerElement = UastContextKt.toUElement(initializer); + if (initializerElement == null) return null; + initializerElement.accept(new AbstractUastVisitor() { @Override - public void visitElement(PsiElement element) { - if (element instanceof PsiLiteralExpression) { - for (PsiReference ref : element.getReferences()) { + public boolean visitLiteralExpression(ULiteralExpression node) { + PsiElement psi = node.getPsi(); + if (psi != null) { + for (PsiReference ref : psi.getReferences()) { if (ref instanceof FileReference) { refs.add((FileReference)ref); } } } - super.visitElement(element); + super.visitLiteralExpression(node); + return true; } }); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiDiamondTypeUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiDiamondTypeUtil.java index f620fc48e5f8..702b3cb8008e 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiDiamondTypeUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiDiamondTypeUtil.java @@ -284,7 +284,13 @@ public class PsiDiamondTypeUtil { private static boolean checkParentApplicability(PsiCallExpression exprCopy) { while (exprCopy != null){ - final JavaResolveResult resolveResult = exprCopy.resolveMethodGenerics(); + JavaResolveResult resolveResult = exprCopy.resolveMethodGenerics(); + if (exprCopy instanceof PsiNewExpression) { + PsiDiamondType diamondType = PsiDiamondType.getDiamondType((PsiNewExpression)exprCopy); + if (diamondType != null) { + resolveResult = diamondType.getStaticFactory(); + } + } if (resolveResult instanceof MethodCandidateInfo && !((MethodCandidateInfo)resolveResult).isApplicable()) { return false; } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSessionContainer.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSessionContainer.java index c17b0f33a383..c03d3c3bf0fc 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSessionContainer.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSessionContainer.java @@ -112,7 +112,7 @@ public class InferenceSessionContainer { } if (session != null) { - final PsiSubstitutor childSubstitutor = inferNested(typeParameters, parameters, arguments, partialSubstitutor, (PsiCall)parent, policy, properties, session); + final PsiSubstitutor childSubstitutor = inferNested(parameters, arguments, (PsiCall)parent, properties, session); if (childSubstitutor != null) return childSubstitutor; } else if (topLevelCall instanceof PsiMethodCallExpression) { @@ -127,14 +127,11 @@ public class InferenceSessionContainer { return inferenceSession.infer(parameters, arguments, parent); } - private static PsiSubstitutor inferNested(final PsiTypeParameter[] typeParameters, - @NotNull final PsiParameter[] parameters, + private static PsiSubstitutor inferNested(@NotNull final PsiParameter[] parameters, @NotNull final PsiExpression[] arguments, - final PsiSubstitutor partialSubstitutor, @NotNull final PsiCall parent, - @NotNull final ParameterTypeInferencePolicy policy, @NotNull final MethodCandidateInfo.CurrentCandidateProperties properties, - final InferenceSession parentSession) { + @NotNull final InferenceSession parentSession) { final CompoundInitialState compoundInitialState = createState(parentSession); InitialInferenceState initialInferenceState = compoundInitialState.getInitialState(parent); if (initialInferenceState != null) { @@ -174,25 +171,12 @@ public class InferenceSessionContainer { if (methodParameters.length == 0) { break; } - final PsiType parameterType = PsiTypesUtil.getParameterType(methodParameters, idx, true); - final PsiType parameterTypeInTermsOfSession = initialInferenceState.getInferenceSubstitutor().substitute(parameterType); - final PsiType lambdaTargetType = compoundInitialState.getInitialSubstitutor().substitute(parameterTypeInTermsOfSession); - if (call.equals(PsiTreeUtil.getParentOfType(parent, PsiCall.class, true))) { - return LambdaUtil.performWithLambdaTargetType((PsiLambdaExpression)gParent, partialSubstitutor.substitute(lambdaTargetType), - () -> { - //parent was mentioned in the top inference session - //just proceed with the target type - final InferenceSession inferenceSession = new InferenceSession(typeParameters, partialSubstitutor, parent.getManager(), parent, policy); - inferenceSession.initExpressionConstraints(parameters, arguments, parent); - return inferenceSession.infer(parameters, arguments, parent); - }); - } //one of the grand parents were found in the top inference session //start from it as it is the top level call final InferenceSession sessionInsideLambda = new InferenceSession(initialInferenceState); sessionInsideLambda.collectAdditionalAndInfer(methodParameters, argumentList.getExpressions(), ((MethodCandidateInfo)result).createProperties(), compoundInitialState.getInitialSubstitutor()); - return inferNested(typeParameters, parameters, arguments, partialSubstitutor, parent, policy, properties, sessionInsideLambda); + return inferNested(parameters, arguments, parent, properties, sessionInsideLambda); } } else { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdasWithInferenceOfReturnTypeInTheLatestLambda.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdasWithInferenceOfReturnTypeInTheLatestLambda.java new file mode 100644 index 000000000000..90d4d743eb3e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdasWithInferenceOfReturnTypeInTheLatestLambda.java @@ -0,0 +1,117 @@ + +import java.util.function.Function; + + +import static java.util.function.Function.identity; + +class Example { + + @FunctionalInterface + public interface Functor { + Functor fmap(Function fn); + } + + @FunctionalInterface + public interface NaturalTransformation, GA extends Functor> extends Function { + static > NaturalTransformation endo( + NaturalTransformation naturalTransformation) { + return naturalTransformation; + } + + static void main(String[] args) { + NaturalTransformation, NatF> happy = + natF -> natF.match(identity(), s -> NatF.s(s.carrier() + 1)); //no problems + + // compiler exception reported on the following line + NaturalTransformation, NatF> unhappy = + NaturalTransformation.endo(natF -> natF.match(identity(), s -> NatF.s(s.carrier() + 1))); + } + } + + public interface Coproduct2 { + R match(Function aFn, Function bFn); + } + + public static abstract class NatF implements Functor, Coproduct2, NatF.S> { + + private NatF() { + } + + public static NatF z() { + return new Z<>(); + } + + public static NatF s(A a) { + return new S<>(a); + } + + public static final class Z extends NatF { + private Z() { + } + + @Override + @SuppressWarnings("unchecked") + public Z fmap(Function fn) { + return (Z) this; + } + + @Override + public R match(Function, ? extends R> aFn, + Function, ? extends R> bFn) { + return aFn.apply(this); + } + } + + public static final class S extends NatF { + private final A a; + + private S(A a) { + this.a = a; + } + + public A carrier() { + return a; + } + + @Override + public S fmap(Function fn) { + return new S<>(fn.apply(carrier())); + } + + @Override + public R match(Function, ? extends R> aFn, + Function, ? extends R> bFn) { + return bFn.apply(this); + } + } + } +} + +class ExampleSimplified { + static void main(String[] args) { + Function, Z> zzFunction = t -> t; + Function, NatF> unhappy = endo(natF -> natF.match(zzFunction, s -> s1(s.carrier()))); + } + + static Function endo(Function function) { + return function; + } + + public static NatF s1(A1 a) { + return null; + } + + public static abstract class NatF { + abstract R match(Function, ? extends R> aFn, + Function, ? extends R> bFn); + } + + public static abstract class Z extends NatF {} + + public static abstract class S extends NatF { + + public A carrier() { + return null; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicit2diamond/beforeInapplicableInference.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicit2diamond/beforeInapplicableInference.java new file mode 100644 index 000000000000..dd0dcc0289b8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/explicit2diamond/beforeInapplicableInference.java @@ -0,0 +1,14 @@ +// "Replace with <>" "false" + +import java.util.function.Consumer; + +class Test { + public static > Foo test(Consumer function) { + return new Foo<T>(function); + } + + public static class Foo> { + Foo(Consumer function) { } + } + +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/suspiciousCalls/NewMapMethods.java b/java/java-tests/testData/inspection/suspiciousCalls/NewMapMethods.java new file mode 100644 index 000000000000..4afcfc6802c7 --- /dev/null +++ b/java/java-tests/testData/inspection/suspiciousCalls/NewMapMethods.java @@ -0,0 +1,11 @@ + +import java.util.Map; + +class Test { + void m(Map map){ + map.getOrDefault(1, ""); + map.getOrDefault("", ""); + map.remove(1, ""); + map.remove("", ""); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java index c8b0151964cc..b188c8bc809b 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java @@ -153,6 +153,7 @@ public class NewLambdaHighlightingTest extends LightDaemonAnalyzerTestCase { public void testCollectLambdaAdditionalConstraintsByGroundType() { doTest(); } public void testInferTypeParametersFromFunctionalInterfaceInputs() { doTest(); } public void testGroundTargetTypeWhenAbstractMethodInSuperclass() { doTest(); } + public void testNestedLambdasWithInferenceOfReturnTypeInTheLatestLambda() { doTest(); } private void doTest() { IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_8, getModule(), getTestRootDisposable()); diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/SuspiciousCollectionMethodCallsTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/SuspiciousCollectionMethodCallsTest.java index 8bef4327f4d1..5b79a7f2e6fd 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/SuspiciousCollectionMethodCallsTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/SuspiciousCollectionMethodCallsTest.java @@ -52,6 +52,10 @@ public class SuspiciousCollectionMethodCallsTest extends LightCodeInsightFixture doTest(); } + public void testNewMapMethods() throws Exception { + doTest(); + } + public void testMethodReferenceWithCollectionCalls() throws Exception { doTest(); } diff --git a/java/java-tests/testSrc/com/intellij/compiler/CompilerReferenceDataInCompletionTest.java b/java/java-tests/testSrc/com/intellij/compiler/CompilerReferenceDataInCompletionTest.java index 37c1acf2cb4e..6b009fd7157e 100644 --- a/java/java-tests/testSrc/com/intellij/compiler/CompilerReferenceDataInCompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/compiler/CompilerReferenceDataInCompletionTest.java @@ -23,6 +23,7 @@ import com.intellij.psi.PsiClass; import com.intellij.psi.PsiField; import com.intellij.psi.PsiMember; import com.intellij.psi.PsiMethod; +import com.intellij.testFramework.SkipSlowTestLocally; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -30,6 +31,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Arrays; import java.util.function.Predicate; +@SkipSlowTestLocally public class CompilerReferenceDataInCompletionTest extends CompilerReferencesTestBase { @Override public void setUp() throws Exception { @@ -50,9 +52,9 @@ public class CompilerReferenceDataInCompletionTest extends CompilerReferencesTes doTestMemberCompletionOrdering(new String[] {"Bar.java", "Foo.java"}, "qwe", "asd(0)", "zxc(0)"); } - public void testOverloads() { - doTestMemberCompletionOrdering(new String[] {"Bar.java", "Foo.java"}, "asd(3)", "asd(0)"); - } + //public void testOverloads() { + // doTestMemberCompletionOrdering(new String[] {"Bar.java", "Foo.java"}, "asd(3)", "asd(0)"); + //} public void testConstructor() { doTestConstructorCompletionOrdering(new String[] {"Foo.java"}, "List l = new ", "LinkedList", "ArrayList"); diff --git a/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java b/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java index 750b310d4700..2c65f7b7ea18 100644 --- a/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java +++ b/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -533,7 +533,7 @@ public class DirectoryIndexTest extends IdeaTestCase { VfsTestUtil.deleteFile(module2TestOutput); final List created = new ArrayList<>(); - VirtualFileListener l = new VirtualFileAdapter() { + VirtualFileListener l = new VirtualFileListener() { @Override public void fileCreated(@NotNull VirtualFileEvent e) { VirtualFile file = e.getFile(); @@ -569,7 +569,7 @@ public class DirectoryIndexTest extends IdeaTestCase { boolean created = new File(myModule1Dir.getPath(), "dir/excluded/foo").mkdirs(); assertTrue(created); - VirtualFileListener l = new VirtualFileAdapter() { + VirtualFileListener l = new VirtualFileListener() { @Override public void fileCreated(@NotNull VirtualFileEvent e) { assertEquals("dir", e.getFileName()); diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterBracesTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterBracesTest.java index 0a5c29b688db..a1678336c6ea 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterBracesTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterBracesTest.java @@ -374,7 +374,7 @@ public class JavaFormatterBracesTest extends AbstractJavaFormatterTest { } public void testLambdaBrace() { - getSettings().BRACE_STYLE = CommonCodeStyleSettings.NEXT_LINE; + getSettings().LAMBDA_BRACE_STYLE = CommonCodeStyleSettings.NEXT_LINE; doMethodTest( "Runnable r = () -> {\n" + "};", @@ -383,5 +383,16 @@ public class JavaFormatterBracesTest extends AbstractJavaFormatterTest { "};" ); } + + public void testLambdaBraceMoveToPrevLine() { + getSettings().LAMBDA_BRACE_STYLE = CommonCodeStyleSettings.END_OF_LINE; + getSettings().KEEP_LINE_BREAKS = false; + doMethodTest( + "Runnable r = () ->\n" + + "{\n" + + "};", + "Runnable r = () -> {\n" + + "};"); + } } diff --git a/java/java-tests/testSrc/com/intellij/refactoring/MovePackageAsDirectoryTest.java b/java/java-tests/testSrc/com/intellij/refactoring/MovePackageAsDirectoryTest.java index 1900861a93d4..bfe186cf3a4c 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/MovePackageAsDirectoryTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/MovePackageAsDirectoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,8 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; +import com.intellij.openapi.vfs.VirtualFileListener; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; @@ -81,7 +81,7 @@ public class MovePackageAsDirectoryTest extends MultiFileTestCase { public void testMultipleClassesInOneFile() throws Exception { final boolean [] fileWasDeleted = new boolean[]{false}; - final VirtualFileAdapter fileAdapter = new VirtualFileAdapter() { + final VirtualFileListener fileAdapter = new VirtualFileListener() { @Override public void fileDeleted(@NotNull VirtualFileEvent event) { fileWasDeleted[0] = !event.getFile().isDirectory(); @@ -179,8 +179,7 @@ public class MovePackageAsDirectoryTest extends MultiFileTestCase { @Override public void performAction(VirtualFile rootDir, VirtualFile rootAfter) throws Exception { final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(myProject); - final Comparator directoryComparator = - (o1, o2) -> o1.getVirtualFile().getPresentableUrl().compareTo(o2.getVirtualFile().getPresentableUrl()); + final Comparator directoryComparator = Comparator.comparing(o -> o.getVirtualFile().getPresentableUrl()); final PsiPackage sourcePackage = psiFacade.findPackage(myPackageName); assertNotNull(sourcePackage); diff --git a/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/ActionHint.java b/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/ActionHint.java index ec9ba3eedbc9..fd1cd6823644 100644 --- a/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/ActionHint.java +++ b/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/ActionHint.java @@ -68,7 +68,8 @@ public class ActionHint { * @return true if this ActionHint checks that some action should be present * or false if it checks that some action should be absent */ - boolean shouldPresent() { + @SuppressWarnings("WeakerAccess") // used in kotlin + public boolean shouldPresent() { return myShouldPresent; } diff --git a/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java b/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java index 834572e91866..0e30f9c371b7 100644 --- a/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java +++ b/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java @@ -312,10 +312,11 @@ final class JavacReferenceCollectorListener implements TaskListener { if (tree == null) return null; Field symField; try { - symField = tree.getClass().getField("sym"); + //should be the same to com.sun.tools.javac.tree.TreeInfo.symbolForImpl() since com.sun.source.util.Trees.getElement() works improperly under jdk 6-7 + symField = tree.getClass().getField(tree instanceof NewClassTree ? "constructor" : "sym"); } catch (NoSuchFieldException e) { - throw new RuntimeException(e); + throw new RuntimeException(tree.getClass().getName()); } try { return (Element) symField.get(tree); diff --git a/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/api/JavacFileData.java b/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/api/JavacFileData.java index 6691b117c785..664cf08360f2 100644 --- a/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/api/JavacFileData.java +++ b/jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/api/JavacFileData.java @@ -158,6 +158,10 @@ public class JavacFileData { return new JavacDef.JavacClassDef(readJavacRef(in), superClasses); case FUN_EXPR_MARKER: return new JavacDef.JavacFunExprDef(readJavacRef(in)); + case METHOD_MARKER: + JavacRef retType = readJavacRef(in); + boolean isStatic = in.readBoolean(); + return new JavacDef.JavacMemberDef(readJavacRef(in), retType, isStatic); default: throw new IllegalStateException("unknown marker " + marker); } @@ -175,6 +179,11 @@ public class JavacFileData { else if (def instanceof JavacDef.JavacFunExprDef) { out.writeByte(FUN_EXPR_MARKER); } + else if (def instanceof JavacDef.JavacMemberDef) { + out.writeByte(METHOD_MARKER); + writeJavacRef(out, ((JavacDef.JavacMemberDef)def).getReturnType()); + out.writeBoolean(((JavacDef.JavacMemberDef)def).isStatic()); + } else { throw new IllegalStateException("unknown type: " + def.getClass()); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java index ab1185aab88b..7244580524e4 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java @@ -80,13 +80,15 @@ public class ClasspathBootstrap { cp.add(getResourcePath(ArtifactRepositoryManager.class)); // aether-dependency-resolver final String aetherPath = getResourcePath(Artifact.class); // aether-1.1.0-all.jar cp.add(aetherPath); - cp.add(FileUtil.toSystemIndependentName(new File(new File(aetherPath).getParentFile(), "maven-aether-provider-3.3.9-all.jar").getAbsolutePath())); // aether-1.1.0-all.jar + cp.add(FileUtil.toSystemIndependentName(new File(new File(aetherPath).getParentFile(), "maven-aether-provider-3.3.9-all.jar").getAbsolutePath())); cp.add(getResourcePath(HttpClient.class)); // httpclient cp.add(getResourcePath(HttpConnection.class)); // httpcore //noinspection UnnecessaryFullyQualifiedName cp.add(getResourcePath(org.apache.commons.codec.binary.Base64.class)); // commons-codec //noinspection UnnecessaryFullyQualifiedName cp.add(getResourcePath(org.apache.commons.logging.LogFactory.class)); // commons-logging + //noinspection UnnecessaryFullyQualifiedName + cp.add(getResourcePath(org.slf4j.Marker.class)); // slf4j cp.addAll(getJavac8RefScannerClasspath()); //don't forget to update CommunityStandaloneJpsBuilder.layoutJps accordingly diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/dependencies/DependencyResolvingBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/dependencies/DependencyResolvingBuilder.java index 543036512a56..788be2cbbe42 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/dependencies/DependencyResolvingBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/dependencies/DependencyResolvingBuilder.java @@ -22,6 +22,7 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.SmartHashSet; import gnu.trove.THashMap; import gnu.trove.THashSet; +import org.eclipse.aether.transfer.TransferCancelledException; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.maven.aether.ArtifactRepositoryManager; import org.jetbrains.idea.maven.aether.ProgressConsumer; @@ -144,6 +145,9 @@ public class DependencyResolvingBuilder extends ModuleLevelBuilder{ } } } + catch (TransferCancelledException e) { + context.checkCanceled(); + } finally { guard.finish(); } @@ -244,6 +248,11 @@ public class DependencyResolvingBuilder extends ModuleLevelBuilder{ public void consume(String message) { context.processMessage(new ProgressMessage(message)); } + + @Override + public boolean isCanceled() { + return context.getCancelStatus().isCanceled(); + } }); // further init manager here MANAGER_KEY.set(context, manager); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java index 360d6519e902..ea712c9a3aaa 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java @@ -25,6 +25,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.SystemProperties; import com.intellij.util.concurrency.SequentialTaskExecutor; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.execution.ParametersListUtil; import com.intellij.util.io.PersistentEnumeratorBase; import gnu.trove.THashMap; import gnu.trove.THashSet; @@ -1026,11 +1027,9 @@ public class JavaBuilder extends ModuleLevelBuilder { } final String customArgs = compilerOptions.ADDITIONAL_OPTIONS_STRING; if (customArgs != null) { - final StringTokenizer customOptsTokenizer = new StringTokenizer(customArgs, " \t\r\n"); boolean skip = false; boolean targetOptionFound = false; - while (customOptsTokenizer.hasMoreTokens()) { - final String userOption = customOptsTokenizer.nextToken(); + for (final String userOption : ParametersListUtil.parse(customArgs)) { if (FILTERED_OPTIONS.contains(userOption)) { skip = true; targetOptionFound = "-target".equals(userOption); diff --git a/json/src/com/intellij/json/JsonBundle.properties b/json/src/com/intellij/json/JsonBundle.properties index e6022beb72a2..23321a4807e3 100644 --- a/json/src/com/intellij/json/JsonBundle.properties +++ b/json/src/com/intellij/json/JsonBundle.properties @@ -25,9 +25,7 @@ inspection.duplicate.keys.name=Duplicate keys in object literals inspection.duplicate.keys.msg.duplicate.keys=Object contains duplicate keys "{0}" # Formatter -formatter.other.tab.group.title=Formatting options -formatter.other.tab.label.align.properties=Align object &properties: -formatter.other.tab.remove.trailing.comma=Remove trailing &comma +formatter.align.properties.caption=Align formatter.align.properties.none=Do not align formatter.align.properties.on.colon=On colon diff --git a/json/src/com/intellij/json/JsonUtil.java b/json/src/com/intellij/json/JsonUtil.java index 6b8bc98ec809..eaa9685b8565 100644 --- a/json/src/com/intellij/json/JsonUtil.java +++ b/json/src/com/intellij/json/JsonUtil.java @@ -1,8 +1,15 @@ package com.intellij.json; +import com.intellij.json.psi.*; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + /** * @author Mikhail Golubev */ @@ -34,4 +41,28 @@ public class JsonUtil { } return null; } + + @Nullable + public static T getPropertyValueOfType(@NotNull final JsonObject object, @NotNull final String name, + @NotNull final Class clazz) { + final JsonProperty property = object.findProperty(name); + if (property == null) return null; + return ObjectUtils.tryCast(property.getValue(), clazz); + } + + @Nullable + public static List getChildAsStringList(@NotNull final JsonObject object, @NotNull final String name) { + final JsonArray array = getPropertyValueOfType(object, name, JsonArray.class); + if (array != null) return array.getValueList().stream().filter(value -> value instanceof JsonStringLiteral) + .map(value -> StringUtil.unquoteString(value.getText())).collect(Collectors.toList()); + return null; + } + + @Nullable + public static List getChildAsSingleStringOrList(@NotNull final JsonObject object, @NotNull final String name) { + final List list = getChildAsStringList(object, name); + if (list != null) return list; + final JsonStringLiteral literal = getPropertyValueOfType(object, name, JsonStringLiteral.class); + return literal == null ? null : Collections.singletonList(StringUtil.unquoteString(literal.getText())); + } } diff --git a/json/src/com/intellij/json/formatter/JsonCodeStylePanel.form b/json/src/com/intellij/json/formatter/JsonCodeStylePanel.form deleted file mode 100644 index cdd5b2afc5f5..000000000000 --- a/json/src/com/intellij/json/formatter/JsonCodeStylePanel.form +++ /dev/null @@ -1,83 +0,0 @@ - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/json/src/com/intellij/json/formatter/JsonCodeStylePanel.java b/json/src/com/intellij/json/formatter/JsonCodeStylePanel.java deleted file mode 100644 index 0b2f593dbbda..000000000000 --- a/json/src/com/intellij/json/formatter/JsonCodeStylePanel.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.intellij.json.formatter; - -import com.intellij.application.options.CodeStyleAbstractPanel; -import com.intellij.json.JsonFileType; -import com.intellij.json.JsonLanguage; -import com.intellij.json.formatter.JsonCodeStyleSettings.PropertyAlignment; -import com.intellij.openapi.editor.colors.EditorColorsScheme; -import com.intellij.openapi.editor.highlighter.EditorHighlighter; -import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.options.ConfigurationException; -import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.testFramework.LightVirtualFile; -import com.intellij.ui.ListCellRendererWrapper; -import com.intellij.ui.components.JBCheckBox; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; - -/** - * @author Mikhail Golubev - */ -public class JsonCodeStylePanel extends CodeStyleAbstractPanel { - public static final String ALIGNMENT_SAMPLE = "{\n" + - " \"foo\": {\n" + - " \"bar\": true,\n" + - " \"baz\": false\n" + - " },\n" + - " \"quux\": [\n" + - " 1, 2.0, 3e0, 4.0e0\n" + - " ],\n" + - " \"longPropertyName\": null\n" + - "}"; - - private JComboBox myPropertiesAlignmentCombo; - private JPanel myPreviewPanel; - private JPanel myPanel; - private JBCheckBox myRemoveTrailingCommaCheckbox; - - @SuppressWarnings("unchecked") - public JsonCodeStylePanel(@NotNull CodeStyleSettings settings) { - super(JsonLanguage.INSTANCE, null, settings); - addPanelToWatch(myPanel); - installPreviewPanel(myPreviewPanel); - - // Initialize combo box with property value alignment types - for (PropertyAlignment alignment : PropertyAlignment.values()) { - myPropertiesAlignmentCombo.addItem(alignment); - } - myPropertiesAlignmentCombo.setRenderer(new ListCellRendererWrapper() { - @Override - public void customize(JList list, PropertyAlignment value, int index, boolean selected, boolean hasFocus) { - setText(value.getDescription()); - } - }); - myPropertiesAlignmentCombo.addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getStateChange() == ItemEvent.SELECTED) { - somethingChanged(); - } - } - }); - } - - @Override - protected int getRightMargin() { - return 80; - } - - @Nullable - @Override - protected EditorHighlighter createHighlighter(EditorColorsScheme scheme) { - return EditorHighlighterFactory.getInstance().createEditorHighlighter(new LightVirtualFile("a.json"), scheme, null); - } - - @NotNull - @Override - protected FileType getFileType() { - return JsonFileType.INSTANCE; - } - - @Nullable - @Override - protected String getPreviewText() { - return ALIGNMENT_SAMPLE; - } - - @Override - public void apply(CodeStyleSettings settings) throws ConfigurationException { - JsonCodeStyleSettings jsonSettings = getCustomSettings(settings); - jsonSettings.PROPERTY_ALIGNMENT = getSelectedAlignmentType().getId(); - jsonSettings.REMOVE_TRAILING_COMMA = myRemoveTrailingCommaCheckbox.isSelected(); - } - - @Override - public boolean isModified(CodeStyleSettings settings) { - JsonCodeStyleSettings jsonSettings = getCustomSettings(settings); - return jsonSettings.PROPERTY_ALIGNMENT != getSelectedAlignmentType().getId() - || jsonSettings.REMOVE_TRAILING_COMMA != myRemoveTrailingCommaCheckbox.isSelected(); - } - - @Nullable - @Override - public JComponent getPanel() { - return myPanel; - } - - @Override - protected void resetImpl(CodeStyleSettings settings) { - JsonCodeStyleSettings jsonSettings = getCustomSettings(settings); - myRemoveTrailingCommaCheckbox.setSelected(jsonSettings.REMOVE_TRAILING_COMMA); - for (int i = 0; i < myPropertiesAlignmentCombo.getItemCount(); i++) { - if (((PropertyAlignment)myPropertiesAlignmentCombo.getItemAt(i)).getId() == jsonSettings.PROPERTY_ALIGNMENT) { - myPropertiesAlignmentCombo.setSelectedIndex(i); - break; - } - } - } - - @NotNull - private PropertyAlignment getSelectedAlignmentType() { - return (PropertyAlignment)myPropertiesAlignmentCombo.getSelectedItem(); - } - - @NotNull - private JsonCodeStyleSettings getCustomSettings(@NotNull CodeStyleSettings settings) { - return settings.getCustomSettings(JsonCodeStyleSettings.class); - } -} diff --git a/json/src/com/intellij/json/formatter/JsonCodeStyleSettings.java b/json/src/com/intellij/json/formatter/JsonCodeStyleSettings.java index 47a4895cc960..fa19c5e641e8 100644 --- a/json/src/com/intellij/json/formatter/JsonCodeStyleSettings.java +++ b/json/src/com/intellij/json/formatter/JsonCodeStyleSettings.java @@ -19,7 +19,7 @@ public class JsonCodeStyleSettings extends CustomCodeStyleSettings { public boolean SPACE_AFTER_COLON = true; public boolean SPACE_BEFORE_COLON = false; - public boolean REMOVE_TRAILING_COMMA = true; + public boolean KEEP_TRAILING_COMMA = false; // TODO: check whether it's possible to migrate CustomCodeStyleSettings to newer com.intellij.util.xmlb.XmlSerializer /** diff --git a/json/src/com/intellij/json/formatter/JsonCodeStyleSettingsProvider.java b/json/src/com/intellij/json/formatter/JsonCodeStyleSettingsProvider.java index a463d3bfdcee..6a54a23351f0 100644 --- a/json/src/com/intellij/json/formatter/JsonCodeStyleSettingsProvider.java +++ b/json/src/com/intellij/json/formatter/JsonCodeStyleSettingsProvider.java @@ -31,7 +31,6 @@ public class JsonCodeStyleSettingsProvider extends CodeStyleSettingsProvider { addSpacesTab(settings); addBlankLinesTab(settings); addWrappingAndBracesTab(settings); - addTab(new JsonCodeStylePanel(settings)); } }; } diff --git a/json/src/com/intellij/json/formatter/JsonLanguageCodeStyleSettingsProvider.java b/json/src/com/intellij/json/formatter/JsonLanguageCodeStyleSettingsProvider.java index 15636d8af88f..6e76e6768339 100644 --- a/json/src/com/intellij/json/formatter/JsonLanguageCodeStyleSettingsProvider.java +++ b/json/src/com/intellij/json/formatter/JsonLanguageCodeStyleSettingsProvider.java @@ -2,26 +2,40 @@ package com.intellij.json.formatter; import com.intellij.application.options.IndentOptionsEditor; import com.intellij.application.options.SmartIndentOptionsEditor; +import com.intellij.json.JsonBundle; import com.intellij.json.JsonLanguage; import com.intellij.lang.Language; import com.intellij.psi.codeStyle.CodeStyleSettingsCustomizable; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider; +import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Arrays; +import java.util.stream.Collectors; + import static com.intellij.psi.codeStyle.CodeStyleSettingsCustomizable.SPACES_OTHER; /** * @author Mikhail Golubev */ public class JsonLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettingsProvider { + private static String[] ALIGN_OPTIONS = Arrays.stream(JsonCodeStyleSettings.PropertyAlignment.values()) + .map(alignment -> alignment.getDescription()) + .toArray(value -> new String[value]); + + private static int[] ALIGN_VALUES = + ArrayUtil.toIntArray(Arrays.stream(JsonCodeStyleSettings.PropertyAlignment.values()) + .map(alignment -> alignment.getId()) + .collect(Collectors.toList())); + private static final String SAMPLE = "{\n" + " \"json literals are\": {\n" + " \"strings\": [\"foo\", \"bar\", \"\\u0062\\u0061\\u0072\"],\n" + " \"numbers\": [42, 6.62606975e-34],\n" + - " \"boolean values\": [true, false],\n" + - " \"and\": {\"null\": null}\n" + + " \"boolean values\": [true, false,],\n" + + " \"objects\": {\"null\": null,\"another\": null,}\n" + " }\n" + "}"; @@ -45,6 +59,11 @@ public class JsonLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSett "WRAP_ON_TYPING", "KEEP_LINE_BREAKS", "WRAP_LONG_LINES"); + + consumer.showCustomOption(JsonCodeStyleSettings.class, + "KEEP_TRAILING_COMMA", + "Trailing comma", + CodeStyleSettingsCustomizable.WRAPPING_KEEP); consumer.showCustomOption(JsonCodeStyleSettings.class, "ARRAY_WRAPPING", @@ -60,6 +79,13 @@ public class JsonLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSett CodeStyleSettingsCustomizable.WRAP_OPTIONS, CodeStyleSettingsCustomizable.WRAP_VALUES); + consumer.showCustomOption(JsonCodeStyleSettings.class, + "PROPERTY_ALIGNMENT", + JsonBundle.message("formatter.align.properties.caption"), + "Objects", + ALIGN_OPTIONS, + ALIGN_VALUES); + } } diff --git a/json/src/com/intellij/json/formatter/JsonTrailingCommaRemover.java b/json/src/com/intellij/json/formatter/JsonTrailingCommaRemover.java index 590f345fd47e..e84ce6943bc4 100644 --- a/json/src/com/intellij/json/formatter/JsonTrailingCommaRemover.java +++ b/json/src/com/intellij/json/formatter/JsonTrailingCommaRemover.java @@ -45,7 +45,7 @@ public class JsonTrailingCommaRemover implements PreFormatProcessor { JsonCodeStyleSettings settings = CodeStyleSettingsManager.getInstance(rootPsi.getProject()) .getCurrentSettings() .getCustomSettings(JsonCodeStyleSettings.class); - if (!settings.REMOVE_TRAILING_COMMA) { + if (settings.KEEP_TRAILING_COMMA) { return range; } PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(rootPsi.getProject()); diff --git a/json/tests/test/com/intellij/json/JsonFormattingTest.java b/json/tests/test/com/intellij/json/JsonFormattingTest.java index 4233546c874c..94ef9d967d93 100644 --- a/json/tests/test/com/intellij/json/JsonFormattingTest.java +++ b/json/tests/test/com/intellij/json/JsonFormattingTest.java @@ -85,7 +85,6 @@ public class JsonFormattingTest extends JsonTestCase { } public void testRemoveTrailingCommas() { - getCustomCodeStyleSettings().REMOVE_TRAILING_COMMA = true; doTest(); } diff --git a/lib/aether-1.1.0-all.jar b/lib/aether-1.1.0-all.jar index 32eb79eac5e2..03da9a3f46c0 100644 Binary files a/lib/aether-1.1.0-all.jar and b/lib/aether-1.1.0-all.jar differ diff --git a/lib/guava-21.0.jar b/lib/guava-21.0.jar index 617753da0966..8f7e0e924153 100644 Binary files a/lib/guava-21.0.jar and b/lib/guava-21.0.jar differ diff --git a/platform/analysis-api/analysis-api.iml b/platform/analysis-api/analysis-api.iml index f2337e3c286d..ac3597a901f5 100644 --- a/platform/analysis-api/analysis-api.iml +++ b/platform/analysis-api/analysis-api.iml @@ -14,5 +14,4 @@
-
- + \ No newline at end of file diff --git a/platform/analysis-impl/analysis-impl.iml b/platform/analysis-impl/analysis-impl.iml index f32f233d0f4e..eafc1b2b89cd 100644 --- a/platform/analysis-impl/analysis-impl.iml +++ b/platform/analysis-impl/analysis-impl.iml @@ -17,5 +17,6 @@ + \ No newline at end of file diff --git a/platform/analysis-impl/src/com/intellij/codeHighlighting/RainbowHighlighter.java b/platform/analysis-impl/src/com/intellij/codeHighlighting/RainbowHighlighter.java index 59f276a2b8db..2da3cfdc5fff 100644 --- a/platform/analysis-impl/src/com/intellij/codeHighlighting/RainbowHighlighter.java +++ b/platform/analysis-impl/src/com/intellij/codeHighlighting/RainbowHighlighter.java @@ -233,16 +233,10 @@ public class RainbowHighlighter { ? minDistanceWithOrdinal : minDistanceWithDiagnostic))); rainbowColors = ContainerUtil.map(rainbowColors, rainbowColor -> resolveConflict(colorCircles, rainbowColor, 0)); - - - final Map cache = getGeneratedTextAttributesCache(colorsScheme); - if (cache != null) { - cache.entrySet().removeIf(entry -> isRainbowTempKey(entry.getKey())); - int i = 0; - for (Color rainbowColor : rainbowColors) { - TextAttributesKey key = createRainbowKey(i++, rainbowColor); - cache.put(key, key.getDefaultAttributes()); - } + int i = 0; + for (Color rainbowColor : rainbowColors) { + TextAttributesKey key = createRainbowKey(i++, rainbowColor); + ((EditorColorsScheme)colorsScheme).setAttributes(key, key.getDefaultAttributes()); } } return rainbowColors.toArray(new Color[rainbowColors.size()]); @@ -259,9 +253,9 @@ public class RainbowHighlighter { final float[] rgb = rgbDiffColor(sampleColor, paletteColor); final double factor = 256 * circle.second / getLength(rgb); final int mod = nestLevel % 4; - final int r = noramalize(sampleColor.getRed() + rgb[0] * factor * (mod == 3 ? 2 : 1)); - final int g = noramalize(sampleColor.getGreen() + rgb[1] * factor * (mod == 1 ? 2 : 1)); - final int b = noramalize(sampleColor.getBlue() + rgb[2] * factor * (mod == 2 ? 2 : 1)); + final int r = normalize(sampleColor.getRed() + rgb[0] * factor * (mod == 3 ? 2 : 1)); + final int g = normalize(sampleColor.getGreen() + rgb[1] * factor * (mod == 1 ? 2 : 1)); + final int b = normalize(sampleColor.getBlue() + rgb[2] * factor * (mod == 2 ? 2 : 1)); final float[] hsbNew = Color.RGBtoHSB(r, g, b, null); final float[] hsbOrig = Color.RGBtoHSB(sampleColor.getRed(), sampleColor.getGreen(), sampleColor.getBlue(), null); @@ -274,7 +268,7 @@ public class RainbowHighlighter { return sampleColor; } - private static int noramalize(double b) { + private static int normalize(double b) { return Math.min(Math.max(1, (int)b), 254); } @@ -308,14 +302,10 @@ public class RainbowHighlighter { @Nullable private static Color[] getColorsFromCache(@NotNull TextAttributesScheme colorsScheme) { - final Map cache = getGeneratedTextAttributesCache(colorsScheme); - if (cache == null) { - return null; - } List colors = new ArrayList<>(); boolean invalidCache = false; for (TextAttributesKey tempKey : RAINBOW_TEMP_KEYS) { - final TextAttributes attributes = cache.get(tempKey); + final TextAttributes attributes = colorsScheme.getAttributes(tempKey); if (attributes == null) { invalidCache = true; break; @@ -328,13 +318,6 @@ public class RainbowHighlighter { return colors.toArray(new Color[colors.size()]); } - @Nullable - private static Map getGeneratedTextAttributesCache(@NotNull TextAttributesScheme colorsScheme) { - return (colorsScheme instanceof EditorGeneratedTextAttributesCache) - ? ((EditorGeneratedTextAttributesCache)colorsScheme).getGeneratedTextAttributesCache() - : null; - } - @Nullable public static Color getRainbowColorFromAttribute(@NotNull TextAttributes attributes) { return attributes.getForegroundColor(); @@ -347,7 +330,9 @@ public class RainbowHighlighter { @NotNull private static TextAttributesKey createRainbowKey(int i, Color rainbowColor) { //noinspection deprecation - TextAttributesKey key = TextAttributesKey.createTextAttributesKey(RAINBOW_TEMP_PREF + i, new TextAttributes()); + TextAttributesKey key = TextAttributesKey.createTempTextAttributesKey( + RAINBOW_TEMP_PREF + i, + new TextAttributes()); key.getDefaultAttributes().setForegroundColor(rainbowColor); return key; } diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ExternalAnnotatorInspectionVisitor.java b/platform/analysis-impl/src/com/intellij/codeInspection/ExternalAnnotatorInspectionVisitor.java index 5450302a2739..f6beb7428985 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ExternalAnnotatorInspectionVisitor.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ExternalAnnotatorInspectionVisitor.java @@ -72,7 +72,7 @@ public class ExternalAnnotatorInspectionVisitor extends PsiElementVisitor { return ProblemDescriptor.EMPTY_ARRAY; } return ReadAction.compute(() -> { - AnnotationHolderImpl annotationHolder = new AnnotationHolderImpl(new AnnotationSession(file)); + AnnotationHolderImpl annotationHolder = new AnnotationHolderImpl(new AnnotationSession(file), true); annotator.apply(file, annotationResult, annotationHolder); return convertToProblemDescriptors(annotationHolder, manager, file); }); diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/InspectionEngine.java b/platform/analysis-impl/src/com/intellij/codeInspection/InspectionEngine.java index 934d804fcb9b..8c6a51472124 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/InspectionEngine.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/InspectionEngine.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import com.intellij.codeInspection.reference.RefManagerImpl; import com.intellij.codeInspection.reference.RefVisitor; import com.intellij.concurrency.JobLauncher; import com.intellij.lang.Language; +import com.intellij.lang.MetaLanguage; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProgressIndicator; @@ -281,15 +282,28 @@ public class InspectionEngine { // unknown language in plugin.xml, ignore result = Collections.singleton(langId); } + else if (language instanceof MetaLanguage) { + Collection matchingLanguages = ((MetaLanguage) language).getMatchingLanguages(); + result = new HashSet<>(); + for (Language matchingLanguage : matchingLanguages) { + result.addAll(getLanguageWithDialects(wrapper, matchingLanguage)); + } + } else { - List dialects = language.getDialects(); - boolean applyToDialects = wrapper.applyToDialects(); - result = applyToDialects && !dialects.isEmpty() ? new THashSet<>(1 + dialects.size()) : new SmartHashSet<>(); - result.add(langId); - if (applyToDialects) { - for (Language dialect : dialects) { - result.add(dialect.getID()); - } + result = getLanguageWithDialects(wrapper, language); + } + return result; + } + + @NotNull + private static Set getLanguageWithDialects(@NotNull LocalInspectionToolWrapper wrapper, Language language) { + Set result;List dialects = language.getDialects(); + boolean applyToDialects = wrapper.applyToDialects(); + result = applyToDialects && !dialects.isEmpty() ? new THashSet<>(1 + dialects.size()) : new SmartHashSet<>(); + result.add(language.getID()); + if (applyToDialects) { + for (Language dialect : dialects) { + result.add(dialect.getID()); } } return result; diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityRepositoryModules.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityRepositoryModules.groovy index 58149968d3a1..54896406ad6d 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityRepositoryModules.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityRepositoryModules.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,7 +50,8 @@ class CommunityRepositoryModules { "xml-analysis-api", "xml-openapi", "xml-psi-api", - "xml-structure-view-api" + "xml-structure-view-api", + "uast-common", ] static List PLATFORM_IMPLEMENTATION_MODULES = [ diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildContextImpl.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildContextImpl.groovy index 11887e4ff67c..1fa1d7b2004a 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildContextImpl.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildContextImpl.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,6 +48,7 @@ class BuildContextImpl extends BuildContext { def context = new BuildContextImpl(compilationContext, productProperties, windowsDistributionCustomizer, linuxDistributionCustomizer, macDistributionCustomizer, proprietaryBuildTools) + setupDependencies(context.messages, communityHome) return context } @@ -75,6 +76,13 @@ class BuildContextImpl extends BuildContext { bootClassPathJarNames = ["bootstrap.jar", "extensions.jar", "util.jar", "jdom.jar", "log4j.jar", "trove4j.jar", "jna.jar"] } + private static void setupDependencies(BuildMessages messages, String communityHome) { + messages.info("Setting up installer dependencies") + if (!BuildUtils.gradle(new File(communityHome, 'build/dependencies/'), 'cleanSetupJbre', 'setupDependencies')) { + messages.error("Cannot setup installer dependencies") + } + } + private String readSnapshotBuildNumber() { new File(paths.communityHome, "build.txt").text.trim() } diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildUtils.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildUtils.groovy index c046138d0864..fbd3309c0fd6 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildUtils.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildUtils.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ package org.jetbrains.intellij.build.impl +import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.text.StringUtil import groovy.transform.CompileStatic @@ -82,4 +83,15 @@ class BuildUtils { return System.out } } + + static boolean gradle(File projectDir, String... tasks) { + def gradleScript = SystemInfo.isWindows ? "gradlew.bat" : "gradlew" + List command = new ArrayList() + command.add("${projectDir.absolutePath}/$gradleScript".toString()) + command.addAll(tasks) + def process = new ProcessBuilder(command).directory(projectDir).start() + process.consumeProcessOutputStream((OutputStream)System.out) + process.consumeProcessErrorStream((OutputStream)System.err) + return process.waitFor() == 0 + } } \ No newline at end of file diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/CompilationContextImpl.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/CompilationContextImpl.groovy index 825ab691ec77..20f0ba0cd0a0 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/CompilationContextImpl.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/CompilationContextImpl.groovy @@ -15,7 +15,6 @@ */ package org.jetbrains.intellij.build.impl -import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.io.FileUtil import groovy.transform.CompileDynamic import groovy.transform.CompileStatic @@ -122,16 +121,9 @@ class CompilationContextImpl implements CompilationContext { } private static void setupDependencies(BuildMessages messages, String communityHome) { - messages.info("Setting up dependencies") - def workingDirectory = new File(communityHome, 'build/dependencies/') - def gradleScript = SystemInfo.isWindows ? "gradlew.bat" : "gradlew" - def process = new ProcessBuilder("${workingDirectory.absolutePath}/$gradleScript", 'cleanSetupJbre', 'setupDependencies') - .directory(workingDirectory) - .start() - process.consumeProcessOutputStream((OutputStream)System.out) - process.consumeProcessErrorStream((OutputStream)System.err) - if (process.waitFor() != 0) { - messages.error("Cannot setup dependencies") + messages.info("Setting up compilation dependencies") + if (!BuildUtils.gradle(new File(communityHome, 'build/dependencies/'), 'setupJdks', 'setupKotlin')) { + messages.error("Cannot setup compilation dependencies") } } diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/TestingTasksImpl.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/TestingTasksImpl.groovy index cc5463e3b044..360b0ba038e0 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/TestingTasksImpl.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/TestingTasksImpl.groovy @@ -152,7 +152,10 @@ class TestingTasksImpl extends TestingTasks { private void runJUnitTask(List jvmArgs, Map systemProperties, List bootstrapClasspath) { defineJunitTask(context.ant, "$context.paths.communityHome/lib") - context.ant.junit(fork: true, showoutput: true, logfailedtests: false) { + String junitTemp = "$context.paths.temp/junit" + context.ant.mkdir(dir: junitTemp) + + context.ant.junit(fork: true, showoutput: true, logfailedtests: false, tempdir: junitTemp) { jvmArgs.each { jvmarg(value: it) } systemProperties.each { key, value -> if (value != null) { diff --git a/platform/core-api/src/com/intellij/lang/MetaLanguage.java b/platform/core-api/src/com/intellij/lang/MetaLanguage.java index f956c9430786..7b0ff92352cb 100644 --- a/platform/core-api/src/com/intellij/lang/MetaLanguage.java +++ b/platform/core-api/src/com/intellij/lang/MetaLanguage.java @@ -19,6 +19,9 @@ import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; import org.jetbrains.annotations.NotNull; +import java.util.Collection; +import java.util.stream.Collectors; + /** * Allows to register a language extension for a group of languages defined by a certain criterion. * To use this, specify the ID of a meta-language in the "language" attribute of an extension in plugin.xml. @@ -41,4 +44,14 @@ public abstract class MetaLanguage extends Language { * Checks if the given language matches the criterion of this meta-language. */ public abstract boolean matchesLanguage(@NotNull Language language); + + /** + * Returns the list of all languages matching this meta-language. + */ + public Collection getMatchingLanguages() { + return Language.getRegisteredLanguages() + .stream() + .filter(language -> matchesLanguage(language)) + .collect(Collectors.toList()); + } } diff --git a/platform/core-api/src/com/intellij/openapi/command/CommandListener.java b/platform/core-api/src/com/intellij/openapi/command/CommandListener.java index 0a0f3fd21052..e30b4bdbe86e 100644 --- a/platform/core-api/src/com/intellij/openapi/command/CommandListener.java +++ b/platform/core-api/src/com/intellij/openapi/command/CommandListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,10 +17,19 @@ package com.intellij.openapi.command; import java.util.EventListener; -public interface CommandListener extends EventListener{ - void commandStarted(CommandEvent event); - void beforeCommandFinished(CommandEvent event); - void commandFinished(CommandEvent event); - void undoTransparentActionStarted(); - void undoTransparentActionFinished(); +public interface CommandListener extends EventListener { + default void commandStarted(CommandEvent event) { + } + + default void beforeCommandFinished(CommandEvent event) { + } + + default void commandFinished(CommandEvent event) { + } + + default void undoTransparentActionStarted() { + } + + default void undoTransparentActionFinished() { + } } \ No newline at end of file diff --git a/platform/core-api/src/com/intellij/openapi/components/BaseComponent.java b/platform/core-api/src/com/intellij/openapi/components/BaseComponent.java index efacb45a9d5f..557049e9b490 100644 --- a/platform/core-api/src/com/intellij/openapi/components/BaseComponent.java +++ b/platform/core-api/src/com/intellij/openapi/components/BaseComponent.java @@ -30,9 +30,8 @@ public interface BaseComponent extends NamedComponent { } /** - * @deprecated Use {@link com.intellij.openapi.Disposable} + * @see {@link com.intellij.openapi.Disposable} */ - @Deprecated default void disposeComponent() { } } diff --git a/platform/core-api/src/com/intellij/openapi/editor/colors/TextAttributesKey.java b/platform/core-api/src/com/intellij/openapi/editor/colors/TextAttributesKey.java index 5bd171d5b6b9..a7efd4cc2797 100644 --- a/platform/core-api/src/com/intellij/openapi/editor/colors/TextAttributesKey.java +++ b/platform/core-api/src/com/intellij/openapi/editor/colors/TextAttributesKey.java @@ -24,10 +24,7 @@ import com.intellij.openapi.util.VolatileNullableLazyValue; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashSet; import org.jdom.Element; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.TestOnly; +import org.jetbrains.annotations.*; import java.util.Set; import java.util.concurrent.ConcurrentMap; @@ -37,6 +34,7 @@ import java.util.concurrent.ConcurrentMap; * A type of item with a distinct highlighting in an editor or in other views. */ public final class TextAttributesKey implements Comparable { + private static final String TEMP_PREFIX = "TEMP::"; private static final Logger LOG = Logger.getInstance(TextAttributesKey.class); private static final TextAttributes NULL_ATTRIBUTES = new TextAttributes(); @@ -164,6 +162,20 @@ public final class TextAttributesKey implements Comparable { return key; } + /** + * Registers a temp text attribute key with the specified identifier and default attributes. + * The attribute of the temp attribute key is not serialized and not copied while TextAttributesScheme + * manipulations. + * + * @param externalName the unique identifier of the key. + * @param defaultAttributes the default text attributes associated with the key. + * @return the new key instance, or an existing instance if the key with the same + * identifier was already registered. + */ + @NotNull + public static TextAttributesKey createTempTextAttributesKey(@NonNls @NotNull String externalName, TextAttributes defaultAttributes) { + return createTextAttributesKey(TEMP_PREFIX + externalName, defaultAttributes); + } /** * Registers a text attribute key with the specified identifier and a fallback key. If text attributes for the key are not defined in @@ -207,6 +219,10 @@ public final class TextAttributesKey implements Comparable { } } + public static boolean isTemp(@NotNull TextAttributesKey key) { + return key.getExternalName().startsWith(TEMP_PREFIX); + } + public interface TextAttributeKeyDefaultsProvider { TextAttributes getDefaultAttributes(TextAttributesKey key); } diff --git a/platform/core-api/src/com/intellij/openapi/vfs/VfsUtilCore.java b/platform/core-api/src/com/intellij/openapi/vfs/VfsUtilCore.java index 6e0b38c3bb6a..fd21a442916e 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/VfsUtilCore.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/VfsUtilCore.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -467,7 +467,7 @@ public class VfsUtilCore { throw new RuntimeException(new IOException(VfsBundle.message("url.parse.error", url.toExternalForm()))); } } - if (SystemInfo.isWindows || SystemInfo.isOS2) { + if (SystemInfo.isWindows) { while (!path.isEmpty() && path.charAt(0) == '/') { path = path.substring(1, path.length()); } diff --git a/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileAdapter.java b/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileAdapter.java index 44998e6dba0e..5cf1046d9234 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileAdapter.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,47 +15,8 @@ */ package com.intellij.openapi.vfs; -import org.jetbrains.annotations.NotNull; - +/** + * @deprecated Use VirtualFileListener directly + */ public abstract class VirtualFileAdapter implements VirtualFileListener { - @Override - public void propertyChanged(@NotNull VirtualFilePropertyEvent event){ - } - - @Override - public void contentsChanged(@NotNull VirtualFileEvent event){ - } - - @Override - public void fileCreated(@NotNull VirtualFileEvent event){ - } - - @Override - public void fileDeleted(@NotNull VirtualFileEvent event){ - } - - @Override - public void fileMoved(@NotNull VirtualFileMoveEvent event){ - } - - @Override - public void fileCopied(@NotNull VirtualFileCopyEvent event) { - fileCreated(event); - } - - @Override - public void beforePropertyChange(@NotNull VirtualFilePropertyEvent event){ - } - - @Override - public void beforeContentsChange(@NotNull VirtualFileEvent event){ - } - - @Override - public void beforeFileDeletion(@NotNull VirtualFileEvent event){ - } - - @Override - public void beforeFileMovement(@NotNull VirtualFileMoveEvent event){ - } } diff --git a/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileContentsChangedAdapter.java b/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileContentsChangedAdapter.java index b36588cd0ee2..36ff8d802a4f 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileContentsChangedAdapter.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileContentsChangedAdapter.java @@ -20,7 +20,7 @@ import org.jetbrains.annotations.NotNull; /** * @author Irina.Chernushina on 1/20/2017. */ -public abstract class VirtualFileContentsChangedAdapter extends VirtualFileAdapter { +public abstract class VirtualFileContentsChangedAdapter implements VirtualFileListener { @Override public void contentsChanged(@NotNull VirtualFileEvent event) { onFileChange(event.getFile()); diff --git a/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileListener.java b/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileListener.java index cb9c82ab5360..675a352072db 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileListener.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ import java.util.EventListener; * Receives notifications about changes in the virtual file system. * * @see VirtualFileManager#addVirtualFileListener(VirtualFileListener) - * @see VirtualFileAdapter + * @see VirtualFileListener */ public interface VirtualFileListener extends EventListener { /** @@ -32,68 +32,78 @@ public interface VirtualFileListener extends EventListener { * * @param event the event object containing information about the change. */ - void propertyChanged(@NotNull VirtualFilePropertyEvent event); + default void propertyChanged(@NotNull VirtualFilePropertyEvent event) { + } /** * Fired when the contents of a virtual file is changed. * * @param event the event object containing information about the change. */ - void contentsChanged(@NotNull VirtualFileEvent event); + default void contentsChanged(@NotNull VirtualFileEvent event) { + } /** * Fired when a virtual file is created. This event is not fired for files discovered during initial VFS initialization. * * @param event the event object containing information about the change. */ - void fileCreated(@NotNull VirtualFileEvent event); + default void fileCreated(@NotNull VirtualFileEvent event) { + } /** * Fired when a virtual file is deleted. * * @param event the event object containing information about the change. */ - void fileDeleted(@NotNull VirtualFileEvent event); + default void fileDeleted(@NotNull VirtualFileEvent event) { + } /** * Fired when a virtual file is moved from within IDEA. * * @param event the event object containing information about the change. */ - void fileMoved(@NotNull VirtualFileMoveEvent event); + default void fileMoved(@NotNull VirtualFileMoveEvent event) { + } /** * Fired when a virtual file is copied from within IDEA. * * @param event the event object containing information about the change. */ - void fileCopied(@NotNull VirtualFileCopyEvent event); + default void fileCopied(@NotNull VirtualFileCopyEvent event) { + } /** * Fired before the change of a name or writable status of a file is processed. * * @param event the event object containing information about the change. */ - void beforePropertyChange(@NotNull VirtualFilePropertyEvent event); + default void beforePropertyChange(@NotNull VirtualFilePropertyEvent event) { + } /** * Fired before the change of contents of a file is processed. * * @param event the event object containing information about the change. */ - void beforeContentsChange(@NotNull VirtualFileEvent event); + default void beforeContentsChange(@NotNull VirtualFileEvent event) { + } /** * Fired before the deletion of a file is processed. * * @param event the event object containing information about the change. */ - void beforeFileDeletion(@NotNull VirtualFileEvent event); + default void beforeFileDeletion(@NotNull VirtualFileEvent event) { + } /** * Fired before the movement of a file is processed. * * @param event the event object containing information about the change. */ - void beforeFileMovement(@NotNull VirtualFileMoveEvent event); + default void beforeFileMovement(@NotNull VirtualFileMoveEvent event) { + } } \ No newline at end of file diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/base/ListenerDiffViewerBase.java b/platform/diff-impl/src/com/intellij/diff/tools/util/base/ListenerDiffViewerBase.java index e9d74176f44e..fa9879242c9d 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/base/ListenerDiffViewerBase.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/base/ListenerDiffViewerBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -78,7 +78,7 @@ public abstract class ListenerDiffViewerBase extends DiffViewerBase { if (files.isEmpty()) return null; - return new VirtualFileAdapter() { + return new VirtualFileListener() { @Override public void contentsChanged(@NotNull VirtualFileEvent event) { if (files.contains(event.getFile())) { diff --git a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java index 951e4e9b7f11..097af8c4fc8a 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java @@ -1198,6 +1198,7 @@ public class DiffUtil { } VirtualFile file = FileDocumentManager.getInstance().getFile(document); if (file != null && file.isValid() && file.isInLocalFileSystem()) { + if (file.getUserData(TEMP_FILE_KEY) == Boolean.TRUE) return false; // decompiled file can be writable, but Document with decompiled content is still read-only return !file.isWritable(); } diff --git a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.kt b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.kt index 0913d32915ca..89a5b12e925a 100644 --- a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.kt +++ b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.kt @@ -113,7 +113,7 @@ class UISettings : BaseState(), PersistentStateComponent { @get:OptionTag("MARK_MODIFIED_TABS_WITH_ASTERISK") var markModifiedTabsWithAsterisk by storedProperty(false) @get:OptionTag("SHOW_TABS_TOOLTIPS") var showTabsTooltips by storedProperty(true) @get:OptionTag("SHOW_DIRECTORY_FOR_NON_UNIQUE_FILENAMES") var showDirectoryForNonUniqueFilenames by storedProperty(true) - var smoothScrolling by storedProperty(SystemInfo.isMac && (SystemInfo.isJetbrainsJvm || SystemInfo.isJavaVersionAtLeast("1.9"))) + var smoothScrolling by storedProperty(SystemInfo.isMac && (SystemInfo.isJetbrainsJvm || SystemInfo.isJavaVersionAtLeast("9"))) @get:OptionTag("NAVIGATE_TO_PREVIEW") var navigateToPreview by storedProperty(false) @get:OptionTag("SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY") var sortLookupElementsLexicographically by storedProperty(false) diff --git a/platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorColorsScheme.java b/platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorColorsScheme.java index 5d015730449f..1c03ce24419b 100644 --- a/platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorColorsScheme.java +++ b/platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorColorsScheme.java @@ -26,7 +26,7 @@ import org.jetbrains.annotations.Nullable; import java.awt.*; -public interface EditorColorsScheme extends Cloneable, TextAttributesScheme, Scheme, SchemeMetaInfo, EditorGeneratedTextAttributesCache { +public interface EditorColorsScheme extends Cloneable, TextAttributesScheme, Scheme, SchemeMetaInfo { @NonNls String DEFAULT_SCHEME_NAME = "Default"; void setName(String name); diff --git a/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java b/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java index fbd28b6ab06f..8a5944dd26f0 100644 --- a/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java +++ b/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java @@ -32,7 +32,6 @@ import com.intellij.openapi.options.SchemeState; import com.intellij.openapi.util.*; import com.intellij.util.JdomKt; import com.intellij.util.PlatformUtils; -import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtilRt; import com.intellij.util.containers.HashMap; import gnu.trove.THashMap; @@ -1009,22 +1008,6 @@ public abstract class AbstractColorsScheme implements EditorColorsScheme, Serial myParentScheme = newParent; } - private volatile Map myAttributesCacheMap; - - @NotNull - @Override - public Map getGeneratedTextAttributesCache() { - if (myAttributesCacheMap == null) { - myAttributesCacheMap = ContainerUtil.newConcurrentMap(); - } - return myAttributesCacheMap; - } - - @Override - public void dropGeneratedTextAttributesCache() { - myAttributesCacheMap = null; - } - void resolveParent(@NotNull Function nameResolver) { if (myParentScheme instanceof TemporaryParent) { String parentName = ((TemporaryParent)myParentScheme).getParentName(); diff --git a/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/EditorColorsSchemeImpl.java b/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/EditorColorsSchemeImpl.java index 77497d82351c..a716846095e2 100644 --- a/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/EditorColorsSchemeImpl.java +++ b/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/EditorColorsSchemeImpl.java @@ -21,25 +21,39 @@ import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.options.ExternalizableScheme; import com.intellij.openapi.util.Comparing; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.*; import java.util.ArrayList; import java.util.Collection; +import java.util.Map; import java.util.function.Function; import static com.intellij.openapi.editor.markup.TextAttributes.USE_INHERITED_MARKER; public class EditorColorsSchemeImpl extends AbstractColorsScheme implements ExternalizableScheme { + private final Map myAttributesTempMap = ContainerUtil.newConcurrentMap(); + public EditorColorsSchemeImpl(EditorColorsScheme parentScheme) { super(parentScheme); } - + + @Override + public void copyTo(AbstractColorsScheme newScheme) { + super.copyTo(newScheme); + myAttributesTempMap.clear(); + } + @Override public void setAttributes(@NotNull TextAttributesKey key, @NotNull TextAttributes attributes) { - if (attributes == USE_INHERITED_MARKER || attributes != getAttributes(key)) { + if (TextAttributesKey.isTemp(key)) { + myAttributesTempMap.put(key, attributes); + } + else if (attributes == USE_INHERITED_MARKER || attributes != getAttributes(key)) { myAttributesMap.put(key, attributes); + myAttributesTempMap.clear(); } } @@ -53,6 +67,10 @@ public class EditorColorsSchemeImpl extends AbstractColorsScheme implements Exte @Override public TextAttributes getAttributes(@Nullable TextAttributesKey key) { if (key != null) { + if (TextAttributesKey.isTemp(key)) { + return myAttributesTempMap.get(key); + } + TextAttributes attributes = getDirectlyDefinedAttributes(key); if (attributes != null && attributes != USE_INHERITED_MARKER) { return attributes; diff --git a/platform/extensions/src/com/intellij/openapi/extensions/impl/ExtensionPointImpl.java b/platform/extensions/src/com/intellij/openapi/extensions/impl/ExtensionPointImpl.java index acf12edd584b..368d055f1dab 100644 --- a/platform/extensions/src/com/intellij/openapi/extensions/impl/ExtensionPointImpl.java +++ b/platform/extensions/src/com/intellij/openapi/extensions/impl/ExtensionPointImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -269,7 +269,10 @@ public class ExtensionPointImpl implements ExtensionPoint { } catch (Exception e) { errorHappened = true; - LOG.error(e); + if (!"org.jetbrains.uast.kotlin.KotlinUastLanguagePlugin".equals(adapter.getAssignableToClassName()) && + !"org.jetbrains.uast.java.JavaUastLanguagePlugin".equals(adapter.getAssignableToClassName())) { + LOG.error(e); + } } myExtensionAdapters.remove(adapter); } diff --git a/platform/extensions/src/com/intellij/openapi/extensions/impl/ExtensionsAreaImpl.java b/platform/extensions/src/com/intellij/openapi/extensions/impl/ExtensionsAreaImpl.java index 9bc711b04228..90944fc8f7dd 100644 --- a/platform/extensions/src/com/intellij/openapi/extensions/impl/ExtensionsAreaImpl.java +++ b/platform/extensions/src/com/intellij/openapi/extensions/impl/ExtensionsAreaImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -277,6 +277,7 @@ public class ExtensionsAreaImpl implements ExtensionsArea { @NotNull PluginDescriptor descriptor, @NotNull ExtensionPoint.Kind kind) { if (hasExtensionPoint(extensionPointName)) { + if (extensionPointName.equals("org.jetbrains.uast.uastLanguagePlugin")) return; final String message = "Duplicate registration for EP: " + extensionPointName + ": original plugin " + getExtensionPoint(extensionPointName).getDescriptor().getPluginId() + ", new plugin " + descriptor.getPluginId(); diff --git a/platform/icons/src/debugger/selfreference.png b/platform/icons/src/debugger/selfreference.png new file mode 100644 index 000000000000..71b18d1765e5 Binary files /dev/null and b/platform/icons/src/debugger/selfreference.png differ diff --git a/platform/icons/src/debugger/selfreference@2x.png b/platform/icons/src/debugger/selfreference@2x.png new file mode 100644 index 000000000000..3f96b3914dfd Binary files /dev/null and b/platform/icons/src/debugger/selfreference@2x.png differ diff --git a/platform/indexing-impl/src/com/intellij/ide/highlighter/custom/SyntaxTable.java b/platform/indexing-impl/src/com/intellij/ide/highlighter/custom/SyntaxTable.java index 5994bc3e7cec..ff78b2e1ccdd 100644 --- a/platform/indexing-impl/src/com/intellij/ide/highlighter/custom/SyntaxTable.java +++ b/platform/indexing-impl/src/com/intellij/ide/highlighter/custom/SyntaxTable.java @@ -16,6 +16,7 @@ package com.intellij.ide.highlighter.custom; import com.intellij.ide.highlighter.custom.tokens.KeywordParser; +import com.intellij.reference.SoftReference; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; @@ -27,10 +28,10 @@ import java.util.Set; * @version 1.0 */ public class SyntaxTable implements Cloneable { - private Set myKeywords1; - private Set myKeywords2; - private Set myKeywords3; - private Set myKeywords4; + private Set myKeywords1 = new THashSet<>(); + private Set myKeywords2 = new THashSet<>(); + private Set myKeywords3 = new THashSet<>(); + private Set myKeywords4 = new THashSet<>(); private String myLineComment = ""; public boolean lineCommentOnlyAtStart; @@ -45,22 +46,16 @@ public class SyntaxTable implements Cloneable { private boolean myHasBrackets; private boolean myHasParens; private boolean myHasStringEscapes; - private volatile KeywordParser myKeywordParser; - - public SyntaxTable() { - myKeywords1 = new THashSet<>(); - myKeywords2 = new THashSet<>(); - myKeywords3 = new THashSet<>(); - myKeywords4 = new THashSet<>(); - } + private volatile SoftReference myKeywordParser; public KeywordParser getKeywordParser() { - KeywordParser parser = myKeywordParser; + KeywordParser parser = SoftReference.dereference(myKeywordParser); if (parser == null) { synchronized (this) { - parser = myKeywordParser; + parser = SoftReference.dereference(myKeywordParser); if (parser == null) { - myKeywordParser = parser = new KeywordParser(Arrays.asList(myKeywords1, myKeywords2, myKeywords3, myKeywords4), myIgnoreCase); + myKeywordParser = new SoftReference<>( + parser = new KeywordParser(Arrays.asList(myKeywords1, myKeywords2, myKeywords3, myKeywords4), myIgnoreCase)); } } } diff --git a/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionResultSet.java b/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionResultSet.java index 8d00c6d3ce46..6742bed18b5b 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionResultSet.java +++ b/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionResultSet.java @@ -62,7 +62,8 @@ public abstract class CompletionResultSet implements Consumer { } /** - * If a given element matches the prefix, give it for further processing (which may eventually result in its appearing in the completion list) + * If a given element matches the prefix, give it for further processing (which may eventually result in its appearing in the completion list). + * @see #addAllElements(Iterable) */ public abstract void addElement(@NotNull final LookupElement element); @@ -70,6 +71,16 @@ public abstract class CompletionResultSet implements Consumer { myConsumer.consume(result); } + /** + * Adds all elements from the given collection that match the prefix for further processing. The elements are processed in batch, + * so that they'll appear in lookup all together.

+ * This can be useful to ensure predictable order of top suggested elements. + * Otherwise, when the lookup is shown, most relevant elements processed to that moment are put to the top + * and remain there even if more relevant elements appear later. + * These "first" elements may differ from completion invocation to completion invocation due to performance fluctuations, + * resulting in varying preselected item in completion and worse user experience. Using {@code addAllElements} + * instead of {@link #addElement(LookupElement)} helps to avoid that. + */ public void addAllElements(@NotNull final Iterable elements) { int seldomCounter = 0; for (LookupElement element : elements) { diff --git a/platform/lang-api/src/com/intellij/execution/filters/UrlFilter.java b/platform/lang-api/src/com/intellij/execution/filters/UrlFilter.java index d325407d2da3..a8d76f88a055 100644 --- a/platform/lang-api/src/com/intellij/execution/filters/UrlFilter.java +++ b/platform/lang-api/src/com/intellij/execution/filters/UrlFilter.java @@ -15,6 +15,7 @@ */ package com.intellij.execution.filters; +import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.util.io.URLUtil; @@ -28,7 +29,7 @@ import java.util.regex.Matcher; /** * @author yole */ -public class UrlFilter implements Filter { +public class UrlFilter implements Filter, DumbAware { @Nullable @Override public Result applyFilter(String line, int entireLength) { diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsCustomizable.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsCustomizable.java index e82ff61e6527..ffb1634077cf 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsCustomizable.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsCustomizable.java @@ -188,6 +188,7 @@ public interface CodeStyleSettingsCustomizable { BRACE_STYLE, CLASS_BRACE_STYLE, METHOD_BRACE_STYLE, + LAMBDA_BRACE_STYLE, USE_FLYING_GEESE_BRACES, FLYING_GEESE_BRACES_GAP, DO_NOT_INDENT_TOP_LEVEL_CLASS_MEMBERS, diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java index c7ad504d9247..fd51fdcd66c1 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java @@ -324,6 +324,7 @@ public class CommonCodeStyleSettings { @BraceStyleConstant public int BRACE_STYLE = END_OF_LINE; @BraceStyleConstant public int CLASS_BRACE_STYLE = END_OF_LINE; @BraceStyleConstant public int METHOD_BRACE_STYLE = END_OF_LINE; + @BraceStyleConstant public int LAMBDA_BRACE_STYLE = END_OF_LINE; /** * Defines if 'flying geese' style should be used for curly braces formatting, e.g. if we want to format code like diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/presentation/CodeStyleSettingPresentation.java b/platform/lang-api/src/com/intellij/psi/codeStyle/presentation/CodeStyleSettingPresentation.java index 1b002b412a78..a7a032b3586f 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/presentation/CodeStyleSettingPresentation.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/presentation/CodeStyleSettingPresentation.java @@ -328,9 +328,10 @@ public class CodeStyleSettingPresentation { new CodeStyleSelectSettingPresentation("METHOD_BRACE_STYLE", ApplicationBundle.message("wrapping.brace.placement.method.declaration"), BRACE_PLACEMENT_VALUES, BRACE_PLACEMENT_OPTIONS), + new CodeStyleSelectSettingPresentation("LAMBDA_BRACE_STYLE", ApplicationBundle.message("wrapping.brace.placement.lambda"), + BRACE_PLACEMENT_VALUES, BRACE_PLACEMENT_OPTIONS), new CodeStyleSelectSettingPresentation("BRACE_STYLE", ApplicationBundle.message("wrapping.brace.placement.other"), BRACE_PLACEMENT_VALUES, BRACE_PLACEMENT_OPTIONS) - )); putGroupTop(result, "EXTENDS_LIST_WRAP", WRAPPING_EXTENDS_LIST, WRAP_VALUES, WRAP_OPTIONS); diff --git a/platform/lang-impl/src/com/intellij/application/options/colors/ColorSchemeActions.java b/platform/lang-impl/src/com/intellij/application/options/colors/ColorSchemeActions.java index 0f89dc4ef762..ee4fd8d46a5a 100644 --- a/platform/lang-impl/src/com/intellij/application/options/colors/ColorSchemeActions.java +++ b/platform/lang-impl/src/com/intellij/application/options/colors/ColorSchemeActions.java @@ -19,6 +19,7 @@ import com.intellij.application.options.schemes.AbstractSchemeActions; import com.intellij.application.options.schemes.AbstractSchemesPanel; import com.intellij.application.options.schemes.SchemeNameGenerator; import com.intellij.openapi.application.ApplicationBundle; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.colors.impl.AbstractColorsScheme; import com.intellij.openapi.editor.colors.impl.DefaultColorsScheme; @@ -29,9 +30,17 @@ import com.intellij.openapi.options.*; import com.intellij.openapi.project.DefaultProjectFactory; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.ListPopup; +import com.intellij.openapi.ui.popup.PopupStep; +import com.intellij.openapi.ui.popup.util.BaseListPopupStep; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; +import org.jdom.Element; import org.jetbrains.annotations.NotNull; +import java.awt.*; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -62,34 +71,104 @@ public abstract class ColorSchemeActions extends AbstractSchemeActions { - String newName = SchemeNameGenerator.getUniqueName(name != null ? name : "Unnamed", - candidate -> getSchemesPanel().getModel() - .containsScheme(candidate, false)); - AbstractColorsScheme newScheme = new EditorColorsSchemeImpl(EmptyColorScheme.INSTANCE); - newScheme.setName(newName); - newScheme.setDefaultMetaInfo(EmptyColorScheme.INSTANCE); - return newScheme; - }); - if (imported != null) { - getOptions().addImportedScheme(imported); - getSchemesPanel() - .showStatus( - ApplicationBundle.message("settings.editor.scheme.import.success", importSource.getPresentableUrl(), imported.getName()), - MessageType.INFO); - } + if ("jar".equals(importSource.getExtension())) { + importFromJar(getSchemesPanel().getToolbar(), importer, importSource); } - catch (SchemeImportException e) { - getSchemesPanel() - .showStatus(ApplicationBundle.message("settings.editor.scheme.import.failure", importSource.getPresentableUrl()), - MessageType.ERROR); + else { + doImport(importer, importSource); } } } } + + private void doImport(@NotNull SchemeImporter importer, @NotNull VirtualFile importSource) { + try { + EditorColorsScheme imported = + importer.importScheme(DefaultProjectFactory.getInstance().getDefaultProject(), importSource, getOptions().getSelectedScheme(), + name -> { + String newName = SchemeNameGenerator.getUniqueName(name != null ? name : "Unnamed", + candidate -> getSchemesPanel().getModel() + .containsScheme(candidate, false)); + AbstractColorsScheme newScheme = new EditorColorsSchemeImpl(EmptyColorScheme.INSTANCE); + newScheme.setName(newName); + newScheme.setDefaultMetaInfo(EmptyColorScheme.INSTANCE); + return newScheme; + }); + if (imported != null) { + getOptions().addImportedScheme(imported); + getSchemesPanel() + .showStatus( + ApplicationBundle.message("settings.editor.scheme.import.success", importSource.getPresentableUrl(), imported.getName()), + MessageType.INFO); + } + } + catch (SchemeImportException e) { + handleError(e, importSource); + } + } + + private void handleError(@NotNull SchemeImportException e, @NotNull VirtualFile importSource) { + String details = e.getMessage(); + getSchemesPanel() + .showStatus( + ApplicationBundle.message("settings.editor.scheme.import.failure", importSource.getPresentableUrl()) + + (StringUtil.isEmpty(details) ? "" : "\n" + details), + MessageType.ERROR); + } + + private void importFromJar(@NotNull Component componentAbove, + @NotNull SchemeImporter importer, + @NotNull VirtualFile jarFile) { + try { + List schemeFiles = getSchemeFiles(jarFile); + if (schemeFiles.size() == 1 || ApplicationManager.getApplication().isUnitTestMode()) { + doImport(importer, schemeFiles.iterator().next()); + return; + } + List> fileList = new ArrayList<>(schemeFiles.size()); + for (VirtualFile file : schemeFiles) { + Element root = SchemeImportUtil.loadSchemeDom(file); + String name = StringUtil.trimStart(ColorSchemeImporter.getSchemeName(root), SchemeManager.EDITABLE_COPY_PREFIX); + fileList.add(Pair.create(name, file)); + } + ListPopup popup = JBPopupFactory.getInstance().createListPopup + (new BaseListPopupStep>("Choose scheme to import", fileList) { + @NotNull + @Override + public String getTextFor(Pair item) { + return item.first; + } + + @Override + public PopupStep onChosen(Pair selectedValue, boolean finalChoice) { + return doFinalStep(() -> doImport(importer, selectedValue.second)); + } + }); + popup.showUnderneathOf(componentAbove); + } + catch (SchemeImportException e) { + handleError(e, jarFile); + } + } + + private static List getSchemeFiles(@NotNull VirtualFile jarFile) throws SchemeImportException { + List schemeFiles = new ArrayList<>(); + for (VirtualFile file : jarFile.getChildren()) { + if (file.isDirectory() && "colors".equals(file.getName())) { + for (VirtualFile schemeFile : file.getChildren()) { + String ext = schemeFile.getExtension(); + if ("icls".equals(ext) || "xml".equals(ext)) { + schemeFiles.add(schemeFile); + } + } + break; + } + } + if (schemeFiles.isEmpty()) { + throw new SchemeImportException("The are no color schemes in the chosen file."); + } + return schemeFiles; + } private boolean tryImportWithImportHandler(@NotNull String importerName) { for (ImportHandler importHandler : Extensions.getExtensions(ImportHandler.EP_NAME)) { diff --git a/platform/lang-impl/src/com/intellij/application/options/colors/ColorSchemeImporter.java b/platform/lang-impl/src/com/intellij/application/options/colors/ColorSchemeImporter.java index 146204a2de78..1c0f378943e6 100644 --- a/platform/lang-impl/src/com/intellij/application/options/colors/ColorSchemeImporter.java +++ b/platform/lang-impl/src/com/intellij/application/options/colors/ColorSchemeImporter.java @@ -33,7 +33,7 @@ import org.jetbrains.annotations.Nullable; public class ColorSchemeImporter implements SchemeImporter { private final static String[] FILE_EXTENSIONS = - new String[] {EditorColorsManager.COLOR_SCHEME_FILE_EXTENSION.substring(1)}; + new String[] {EditorColorsManager.COLOR_SCHEME_FILE_EXTENSION.substring(1), "jar"}; @NotNull @Override @@ -56,7 +56,7 @@ public class ColorSchemeImporter implements SchemeImporter { return scheme; } - private static String getSchemeName(@NotNull Element root) throws SchemeImportException { + static String getSchemeName(@NotNull Element root) throws SchemeImportException { String name = root.getAttributeValue("name"); if (name == null) throw new SchemeImportException("Scheme 'name' attribute is missing."); return name; diff --git a/platform/lang-impl/src/com/intellij/application/options/colors/SimpleEditorPreview.java b/platform/lang-impl/src/com/intellij/application/options/colors/SimpleEditorPreview.java index b4b9e0ca947a..5ecf40f45011 100644 --- a/platform/lang-impl/src/com/intellij/application/options/colors/SimpleEditorPreview.java +++ b/platform/lang-impl/src/com/intellij/application/options/colors/SimpleEditorPreview.java @@ -332,7 +332,6 @@ public class SimpleEditorPreview implements PreviewPanel { public void setupRainbow(@NotNull EditorColorsScheme colorsScheme, @NotNull RainbowColorSettingsPage page) { final List initialMarkup = new ArrayList<>(); myHighlightsExtractor.extractHighlights(page.getDemoText(), initialMarkup); - colorsScheme.dropGeneratedTextAttributesCache(); final List rainbowMarkup = setupRainbowHighlighting( page, initialMarkup, diff --git a/platform/lang-impl/src/com/intellij/application/options/schemes/SchemesCombo.java b/platform/lang-impl/src/com/intellij/application/options/schemes/SchemesCombo.java index c8fb72f2158f..95bb71ab4751 100644 --- a/platform/lang-impl/src/com/intellij/application/options/schemes/SchemesCombo.java +++ b/platform/lang-impl/src/com/intellij/application/options/schemes/SchemesCombo.java @@ -19,6 +19,7 @@ import com.intellij.openapi.options.Scheme; import com.intellij.openapi.options.SchemeManager; import com.intellij.openapi.ui.ComboBox; import com.intellij.ui.*; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -116,19 +117,6 @@ public abstract class SchemesCombo extends ComboBox> { - private ListCellRendererWrapper myWrapper = new ListCellRendererWrapper() { - @Override - public void customize(JList list, - MySchemeListItem value, - int index, - boolean selected, - boolean hasFocus) { - if (value.isSeparator()) { - setText(" Stored in " + value.getPresentableText()); - setSeparator(); - } - } - }; @Override public Component getListCellRendererComponent(JList> list, @@ -136,14 +124,15 @@ public abstract class SchemesCombo extends ComboBox extends ComboBox getContents() { diff --git a/platform/lang-impl/src/com/intellij/openapi/diff/impl/settings/DiffPreviewProvider.java b/platform/lang-impl/src/com/intellij/openapi/diff/impl/settings/DiffPreviewProvider.java index 6ad6cb479202..f1fe07b82d07 100644 --- a/platform/lang-impl/src/com/intellij/openapi/diff/impl/settings/DiffPreviewProvider.java +++ b/platform/lang-impl/src/com/intellij/openapi/diff/impl/settings/DiffPreviewProvider.java @@ -51,9 +51,9 @@ public abstract class DiffPreviewProvider { @NotNull String right, @NotNull FileType fileType) { return new DiffContent[]{ - createContent(left, StdFileTypes.JAVA), - createContent(center, StdFileTypes.JAVA), - createContent(right, StdFileTypes.JAVA)}; + createContent(left, fileType), + createContent(center, fileType), + createContent(right, fileType)}; } @NotNull diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java index ee2c47a58936..d3225ee1bcf1 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java @@ -37,7 +37,10 @@ import com.intellij.openapi.roots.WatchedRootsProvider; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.*; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.StandardFileSystems; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.ex.VirtualFileManagerAdapter; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; import com.intellij.openapi.vfs.pointers.VirtualFilePointer; @@ -115,16 +118,6 @@ public class ProjectRootManagerComponent extends ProjectRootManagerImpl implemen myDoLogCachesUpdate = ApplicationManager.getApplication().isInternal() && !ApplicationManager.getApplication().isUnitTestMode(); } - @Override - public void disposeComponent() { - } - - @NotNull - @Override - public String getComponentName() { - return "ProjectRootManager"; - } - @Override public void initComponent() { myConnection.subscribe(BatchUpdateListener.TOPIC, myHandler); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java b/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java index 713f53d94bd3..c7d502240e17 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,7 +53,7 @@ import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; -public class PsiVFSListener extends VirtualFileAdapter { +public class PsiVFSListener implements VirtualFileListener { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.file.impl.PsiVFSListener"); private final FileTypeManager myFileTypeManager; diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/SemanticEditorPosition.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/SemanticEditorPosition.java index b679bef461b6..894b6f4d47e7 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/SemanticEditorPosition.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/SemanticEditorPosition.java @@ -18,6 +18,8 @@ package com.intellij.psi.impl.source.codeStyle; import com.intellij.lang.Language; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.editor.highlighter.HighlighterIterator; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.psi.tree.IElementType; import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; @@ -107,8 +109,17 @@ public abstract class SemanticEditorPosition { } public SemanticEditorPosition findLeftParenthesisBackwardsSkippingNested(@NotNull SyntaxElement leftParenthesis, - @NotNull SyntaxElement rightParenthesis) { + @NotNull SyntaxElement rightParenthesis) { + return findLeftParenthesisBackwardsSkippingNested(leftParenthesis, rightParenthesis, Conditions.alwaysFalse()); + } + + public SemanticEditorPosition findLeftParenthesisBackwardsSkippingNested(@NotNull SyntaxElement leftParenthesis, + @NotNull SyntaxElement rightParenthesis, + @NotNull Condition terminationCondition) { while (!myIterator.atEnd()) { + if (terminationCondition.value(map(myIterator.getTokenType()))) { + break; + } if (rightParenthesis.equals(map(myIterator.getTokenType()))) { beforeParentheses(leftParenthesis, rightParenthesis); } @@ -173,7 +184,7 @@ public abstract class SemanticEditorPosition { } public boolean hasEmptyLineAfter(int offset) { - for (int i = offset; i < myIterator.getEnd(); i ++) { + for (int i = offset + 1; i < myIterator.getEnd(); i++) { if (myChars.charAt(i) == '\n') return true; } return false; diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/lineIndent/JavaLikeLangLineIndentProvider.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/lineIndent/JavaLikeLangLineIndentProvider.java index 615cfe1e7fcb..f3dfc8767b7f 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/lineIndent/JavaLikeLangLineIndentProvider.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/lineIndent/JavaLikeLangLineIndentProvider.java @@ -34,8 +34,10 @@ import static com.intellij.formatting.Indent.Type.*; import static com.intellij.psi.impl.source.codeStyle.lineIndent.JavaLikeLangLineIndentProvider.JavaLikeElement.*; /** - * A base class Java-like language line indent provider. If JavaLikeLangLineIndentProvider is unable to calculate - * the indentation, it forwards the request to FormatterBasedLineIndentProvider. + * A base class for Java-like language line indent provider. + * If a LineIndentProvider is not provided, {@link FormatterBasedLineIndentProvider} is used. + * If a registered provider is unable to calculate the indentation, + * {@link FormatterBasedIndentAdjuster} will be used. */ public abstract class JavaLikeLangLineIndentProvider implements LineIndentProvider{ @@ -90,8 +92,12 @@ public abstract class JavaLikeLangLineIndentProvider implements LineIndentProvid SemanticEditorPosition position = getPosition(editor,offset); if (position.hasEmptyLineAfter(offset) && !position.after().isAtAnyOf(ArrayClosingBracket, BlockOpeningBrace, BlockClosingBrace, RightParenthesis) && - !position.isAtEnd()) { - return myFactory.createIndentCalculator(NONE, IndentCalculator.LINE_AFTER); + !position.isAtEnd() && + position.findLeftParenthesisBackwardsSkippingNested(LeftParenthesis, RightParenthesis, + element -> element == BlockClosingBrace || element == BlockOpeningBrace || + element == Semicolon) + .isAt(LeftParenthesis)) { + return myFactory.createIndentCalculator(NONE, IndentCalculator.LINE_AFTER); } } else if (getPosition(editor, offset + 1).matchesRule( diff --git a/platform/lang-impl/src/com/intellij/refactoring/move/moveFilesOrDirectories/MoveFilesOrDirectoriesProcessor.java b/platform/lang-impl/src/com/intellij/refactoring/move/moveFilesOrDirectories/MoveFilesOrDirectoriesProcessor.java index 22113eeb361d..511316f8ea35 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/move/moveFilesOrDirectories/MoveFilesOrDirectoriesProcessor.java +++ b/platform/lang-impl/src/com/intellij/refactoring/move/moveFilesOrDirectories/MoveFilesOrDirectoriesProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,6 +44,7 @@ import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -204,13 +205,11 @@ public class MoveFilesOrDirectoriesProcessor extends BaseRefactoringProcessor { } catch (IncorrectOperationException e) { - final String message = e.getMessage(); - final int index = message != null ? message.indexOf("java.io.IOException") : -1; - if (index >= 0 && message != null) { - ApplicationManager.getApplication().invokeLater(() -> { - String cause = message.substring(index + "java.io.IOException".length()); - Messages.showMessageDialog(myProject, cause, RefactoringBundle.message("error.title"), Messages.getErrorIcon()); - }); + Throwable cause = e.getCause(); + if (cause instanceof IOException) { + LOG.info(e); + ApplicationManager.getApplication().invokeLater( + () -> Messages.showMessageDialog(myProject, cause.getMessage(), RefactoringBundle.message("error.title"), Messages.getErrorIcon())); } else { LOG.error(e); diff --git a/platform/lang-impl/src/com/intellij/ui/debugger/extensions/PlaybackDebugger.java b/platform/lang-impl/src/com/intellij/ui/debugger/extensions/PlaybackDebugger.java index fffa86acecf6..de932424c287 100644 --- a/platform/lang-impl/src/com/intellij/ui/debugger/extensions/PlaybackDebugger.java +++ b/platform/lang-impl/src/com/intellij/ui/debugger/extensions/PlaybackDebugger.java @@ -80,7 +80,7 @@ public class PlaybackDebugger implements UiDebuggerExtension, PlaybackRunner.Sta private final JTextField myCurrentScript = new JTextField(); - private VirtualFileAdapter myVfsListener; + private VirtualFileListener myVfsListener; private boolean myChanged; @@ -156,7 +156,7 @@ public class PlaybackDebugger implements UiDebuggerExtension, PlaybackRunner.Sta myComponent.add(script2Log, BorderLayout.CENTER); - myVfsListener = new VirtualFileAdapter() { + myVfsListener = new VirtualFileListener() { @Override public void contentsChanged(@NotNull VirtualFileEvent event) { final VirtualFile file = pathToFile(); @@ -341,13 +341,9 @@ public class PlaybackDebugger implements UiDebuggerExtension, PlaybackRunner.Sta final Component c = ((WindowManagerEx)WindowManager.getInstance()).getFocusedComponent(frame); if (c != null) { - IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> { - IdeFocusManager.getGlobalInstance().requestFocus(c, true); - }); + IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> IdeFocusManager.getGlobalInstance().requestFocus(c, true)); } else { - IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> { - IdeFocusManager.getGlobalInstance().requestFocus(frame, true); - }); + IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> IdeFocusManager.getGlobalInstance().requestFocus(frame, true)); } //noinspection SSBasedInspection @@ -355,7 +351,7 @@ public class PlaybackDebugger implements UiDebuggerExtension, PlaybackRunner.Sta } - private IdeFrameImpl getFrame() { + private static IdeFrameImpl getFrame() { final Frame[] all = Frame.getFrames(); for (Frame each : all) { if (each instanceof IdeFrame) { diff --git a/platform/lang-impl/src/com/intellij/util/indexing/IndexedFilesListener.java b/platform/lang-impl/src/com/intellij/util/indexing/IndexedFilesListener.java index 0c758c880ae1..039c47492ed1 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/IndexedFilesListener.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/IndexedFilesListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ import java.io.File; import java.io.IOException; import java.util.List; -public abstract class IndexedFilesListener extends VirtualFileAdapter implements BulkFileListener { +public abstract class IndexedFilesListener implements BulkFileListener, VirtualFileListener { private final ManagingFS myManagingFS = ManagingFS.getInstance(); @Nullable private final String myConfigPath; @Nullable private final String myLogPath; diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.java b/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.java index 64b6ad6d773a..bfb97f137f19 100644 --- a/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.java +++ b/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,8 +33,8 @@ import org.jetbrains.annotations.Nullable; import java.util.List; -public class LocalHistoryEventDispatcher extends VirtualFileAdapter implements VirtualFileManagerListener, CommandListener, - BulkFileListener { +public class LocalHistoryEventDispatcher implements VirtualFileManagerListener, CommandListener, + BulkFileListener, VirtualFileListener { private static final Key WAS_VERSIONED_KEY = Key.create(LocalHistoryEventDispatcher.class.getSimpleName() + ".WAS_VERSIONED_KEY"); diff --git a/platform/lvcs-impl/src/com/intellij/openapi/command/impl/FileUndoProvider.java b/platform/lvcs-impl/src/com/intellij/openapi/command/impl/FileUndoProvider.java index a1083c00d80b..500f87d7841c 100644 --- a/platform/lvcs-impl/src/com/intellij/openapi/command/impl/FileUndoProvider.java +++ b/platform/lvcs-impl/src/com/intellij/openapi/command/impl/FileUndoProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,7 @@ import org.jetbrains.annotations.NotNull; import java.io.IOException; -public class FileUndoProvider extends VirtualFileAdapter implements UndoProvider { +public class FileUndoProvider implements UndoProvider, VirtualFileListener { public static final Logger LOG = Logger.getInstance("#" + FileUndoProvider.class.getName()); private final Key DELETION_WAS_UNDOABLE = new Key<>(FileUndoProvider.class.getName() + ".DeletionWasUndoable"); diff --git a/platform/platform-api/src/com/intellij/openapi/command/CommandAdapter.java b/platform/platform-api/src/com/intellij/openapi/command/CommandAdapter.java index 6a6e52e97216..266bc9e30a5f 100644 --- a/platform/platform-api/src/com/intellij/openapi/command/CommandAdapter.java +++ b/platform/platform-api/src/com/intellij/openapi/command/CommandAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,10 @@ */ package com.intellij.openapi.command; -public abstract class CommandAdapter implements CommandListener{ +/** + * @deprecated Use CommandListener directly + */ +public abstract class CommandAdapter implements CommandListener { @Override public void commandStarted(CommandEvent event) { } diff --git a/platform/platform-api/src/com/intellij/openapi/options/SchemeImportException.java b/platform/platform-api/src/com/intellij/openapi/options/SchemeImportException.java index 0582771f5a3b..72061799f8bd 100644 --- a/platform/platform-api/src/com/intellij/openapi/options/SchemeImportException.java +++ b/platform/platform-api/src/com/intellij/openapi/options/SchemeImportException.java @@ -18,11 +18,16 @@ package com.intellij.openapi.options; /** * Thrown from SchemeImporter when a scheme can't be read for some reason. * - * @see com.intellij.openapi.options.SchemeImporter + * @see SchemeImporter * @author Rustam Vishnyakov */ public class SchemeImportException extends Exception { boolean myIsWarning; + + public SchemeImportException() { + super(); + } + public SchemeImportException(String message) { super(message); } diff --git a/platform/platform-api/src/com/intellij/openapi/ui/FixedSizeButton.java b/platform/platform-api/src/com/intellij/openapi/ui/FixedSizeButton.java index 036e574263ea..c4d62c4f5d0d 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/FixedSizeButton.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/FixedSizeButton.java @@ -16,6 +16,7 @@ package com.intellij.openapi.ui; import com.intellij.icons.AllIcons; +import com.intellij.openapi.util.SystemInfo; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; @@ -88,10 +89,11 @@ public class FixedSizeButton extends JButton { public Dimension getPreferredSize() { if (myComponent != null) { int size = myComponent.getPreferredSize().height; - if (myComponent instanceof JComboBox && (UIUtil.isUnderIntelliJLaF() || UIUtil.isUnderDarcula())) { + if (myComponent instanceof JComboBox && (!SystemInfo.isMac && UIUtil.isUnderIntelliJLaF() || UIUtil.isUnderDarcula())) { // JComboBox's preferred height is 2px greater than JTextField's one, because // javax.swing.DefaultListCellRenderer#getNoFocusBorder returns (1,1,1,1) border. // Decrement to have equal sizes for pretty look when stacked vertically + // Mac default border is computed correctly, so don't account for on Mac. size -= 2; } return new Dimension(size, size); diff --git a/platform/platform-api/src/com/intellij/ui/components/breadcrumbs/Breadcrumbs.java b/platform/platform-api/src/com/intellij/ui/components/breadcrumbs/Breadcrumbs.java index c28cbd9d8bad..393569045ac5 100644 --- a/platform/platform-api/src/com/intellij/ui/components/breadcrumbs/Breadcrumbs.java +++ b/platform/platform-api/src/com/intellij/ui/components/breadcrumbs/Breadcrumbs.java @@ -20,9 +20,7 @@ import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.editor.markup.EffectType; import com.intellij.openapi.editor.markup.TextAttributes; -import com.intellij.openapi.util.registry.Registry; import com.intellij.ui.ColorUtil; -import com.intellij.ui.Gray; import com.intellij.ui.paint.EffectPainter; import com.intellij.ui.paint.RectanglePainter; import com.intellij.util.ui.AbstractLayoutManager; @@ -37,6 +35,7 @@ import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; +import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.Rectangle; @@ -69,7 +68,7 @@ public class Breadcrumbs extends JComponent implements RegionPainter { addMouseListener(containerMouseHandler); addMouseMotionListener(containerMouseHandler); setLayout(STATELESS_LAYOUT); - setForeground(Gray.x92); + setOpaque(true); } public void onHover(BiConsumer consumer) { @@ -132,6 +131,17 @@ public class Breadcrumbs extends JComponent implements RegionPainter { repaint(); } + @Override + protected void paintComponent(Graphics g) { + // this custom component does not have a corresponding UI, + // so we should care of painting its background + if (isOpaque()) { + g.setColor(getBackground()); + g.fillRect(0, 0, getWidth(), getHeight()); + } + super.paintComponent(g); + } + @Override public void paint(Graphics2D g, int x, int y, int width, int height, Crumb crumb) { int scale = getScale(this); @@ -174,8 +184,7 @@ public class Breadcrumbs extends JComponent implements RegionPainter { if (thickness > 0) { Color foreground = getForeground(crumb); if (foreground != null) { - double alpha = Registry.doubleValue("editor.breadcrumbs.alpha"); - g.setColor(ColorUtil.toAlpha(foreground, (int)(alpha * foreground.getAlpha()))); + g.setColor(ColorUtil.toAlpha(foreground, (int)(.6 * foreground.getAlpha()))); paint(g, x, y, width, height, thickness); } } diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaCheckBoxUI.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaCheckBoxUI.java index 9194be86566a..458153ccd66e 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaCheckBoxUI.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaCheckBoxUI.java @@ -225,6 +225,6 @@ public class DarculaCheckBoxUI extends MetalCheckBoxUI { @Override public Icon getDefaultIcon() { - return JBUI.scale(EmptyIcon.create(20)).asUIResource(); + return EmptyIcon.create(JBUI.scale(20)).asUIResource(); } } diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/browseButton.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/browseButton.png deleted file mode 100644 index e85a8e49a1d5..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/browseButton.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/browseButton@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/browseButton@2x.png deleted file mode 100644 index f663b23a2e41..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/browseButton@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonComboLeft.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonComboLeft.png deleted file mode 100644 index d99be4d5ab00..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonComboLeft.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonComboLeft@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonComboLeft@2x.png deleted file mode 100644 index 937c8268b6d2..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonComboLeft@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonComboMiddle.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonComboMiddle.png deleted file mode 100644 index 1f84b0eb2383..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonComboMiddle.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonComboMiddle@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonComboMiddle@2x.png deleted file mode 100644 index 7ff520f1f15a..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonComboMiddle@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonComboRight.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonComboRight.png deleted file mode 100644 index dbfe3db00c08..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonComboRight.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonComboRight@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonComboRight@2x.png deleted file mode 100644 index 0b720a9e2384..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonComboRight@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeft.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeft.png deleted file mode 100644 index 3dc0fb1f2418..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeft.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeft@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeft@2x.png deleted file mode 100644 index 943f526e0a85..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeft@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeftFocused.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeftFocused.png deleted file mode 100644 index 70ba7a202a6f..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeftFocused.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeftFocused@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeftFocused@2x.png deleted file mode 100644 index 632420094f6f..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeftFocused@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeftSelected.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeftSelected.png deleted file mode 100644 index ce8ef940f9f0..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeftSelected.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeftSelected@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeftSelected@2x.png deleted file mode 100644 index 3e3ba77cf7bd..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeftSelected@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeftSelectedFocused.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeftSelectedFocused.png deleted file mode 100644 index 4ca54c232e3d..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeftSelectedFocused.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeftSelectedFocused@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeftSelectedFocused@2x.png deleted file mode 100644 index d60caaba84f1..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonLeftSelectedFocused@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddle.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddle.png deleted file mode 100644 index e93b2eb1d954..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddle.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddle@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddle@2x.png deleted file mode 100644 index c06144ad474a..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddle@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddleFocused.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddleFocused.png deleted file mode 100644 index c6aba3e96cd2..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddleFocused.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddleFocused@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddleFocused@2x.png deleted file mode 100644 index 02fa73a787dd..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddleFocused@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddleSelected.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddleSelected.png deleted file mode 100644 index 417b30b4c7b5..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddleSelected.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddleSelected@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddleSelected@2x.png deleted file mode 100644 index dcab112b120b..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddleSelected@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddleSelectedFocused.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddleSelectedFocused.png deleted file mode 100644 index ce4ee9b76e46..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddleSelectedFocused.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddleSelectedFocused@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddleSelectedFocused@2x.png deleted file mode 100644 index 258ab92c4ddb..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonMiddleSelectedFocused@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRight.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRight.png deleted file mode 100644 index 4d18a8c8cb49..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRight.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRight@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRight@2x.png deleted file mode 100644 index 9cac08480859..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRight@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRightFocused.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRightFocused.png deleted file mode 100644 index fcbeba38d7b8..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRightFocused.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRightFocused@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRightFocused@2x.png deleted file mode 100644 index c96dc1e51491..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRightFocused@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRightSelected.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRightSelected.png deleted file mode 100644 index ef1759ba03dc..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRightSelected.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRightSelected@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRightSelected@2x.png deleted file mode 100644 index 45bdb1021717..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRightSelected@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRightSelectedFocused.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRightSelectedFocused.png deleted file mode 100644 index db9b7e2cd6f2..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRightSelectedFocused.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRightSelectedFocused@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRightSelectedFocused@2x.png deleted file mode 100644 index 556058bc0924..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/buttonRightSelectedFocused@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/browseButton.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/browseButton.png deleted file mode 100644 index e85a8e49a1d5..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/browseButton.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/browseButton@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/browseButton@2x.png deleted file mode 100644 index f663b23a2e41..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/browseButton@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonComboLeft.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonComboLeft.png deleted file mode 100644 index d99be4d5ab00..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonComboLeft.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonComboLeft@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonComboLeft@2x.png deleted file mode 100644 index 937c8268b6d2..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonComboLeft@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonComboMiddle.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonComboMiddle.png deleted file mode 100644 index 1f84b0eb2383..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonComboMiddle.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonComboMiddle@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonComboMiddle@2x.png deleted file mode 100644 index 7ff520f1f15a..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonComboMiddle@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonComboRight.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonComboRight.png deleted file mode 100644 index dbfe3db00c08..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonComboRight.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonComboRight@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonComboRight@2x.png deleted file mode 100644 index 0b720a9e2384..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonComboRight@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeft.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeft.png deleted file mode 100644 index a0360b323836..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeft.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeft@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeft@2x.png deleted file mode 100644 index 943f526e0a85..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeft@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeftFocused.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeftFocused.png deleted file mode 100644 index d9f7824c07ba..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeftFocused.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeftFocused@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeftFocused@2x.png deleted file mode 100644 index 539c29ddf8b9..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeftFocused@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeftSelected.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeftSelected.png deleted file mode 100644 index 4716ced7977a..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeftSelected.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeftSelected@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeftSelected@2x.png deleted file mode 100644 index e88963eee3d3..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeftSelected@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeftSelectedFocused.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeftSelectedFocused.png deleted file mode 100644 index c2cb61084721..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeftSelectedFocused.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeftSelectedFocused@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeftSelectedFocused@2x.png deleted file mode 100644 index 5113f9633fab..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonLeftSelectedFocused@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddle.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddle.png deleted file mode 100644 index 5e6e01824d84..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddle.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddle@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddle@2x.png deleted file mode 100644 index c06144ad474a..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddle@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddleFocused.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddleFocused.png deleted file mode 100644 index b310695df72b..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddleFocused.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddleFocused@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddleFocused@2x.png deleted file mode 100644 index 8f9926a0b6cb..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddleFocused@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddleSelected.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddleSelected.png deleted file mode 100644 index ee5e439d2dea..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddleSelected.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddleSelected@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddleSelected@2x.png deleted file mode 100644 index 82925d5a9b20..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddleSelected@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddleSelectedFocused.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddleSelectedFocused.png deleted file mode 100644 index 18e21bb5007d..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddleSelectedFocused.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddleSelectedFocused@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddleSelectedFocused@2x.png deleted file mode 100644 index 38bc40fcaaf1..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonMiddleSelectedFocused@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRight.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRight.png deleted file mode 100644 index 29d733d404da..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRight.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRight@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRight@2x.png deleted file mode 100644 index 9cac08480859..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRight@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRightFocused.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRightFocused.png deleted file mode 100644 index dbd1b76e4407..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRightFocused.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRightFocused@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRightFocused@2x.png deleted file mode 100644 index f8f3af00cb67..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRightFocused@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRightSelected.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRightSelected.png deleted file mode 100644 index ef0375895631..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRightSelected.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRightSelected@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRightSelected@2x.png deleted file mode 100644 index 0c7b3d1d51b6..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRightSelected@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRightSelectedFocused.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRightSelectedFocused.png deleted file mode 100644 index e5e6f7f1cd52..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRightSelectedFocused.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRightSelectedFocused@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRightSelectedFocused@2x.png deleted file mode 100644 index 7aebb28d50f1..000000000000 Binary files a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/graphite/buttonRightSelectedFocused@2x.png and /dev/null differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJButtonBorder.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJButtonBorder.java index 4d44846101cf..e264f0a29840 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJButtonBorder.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJButtonBorder.java @@ -15,24 +15,38 @@ */ package com.intellij.ide.ui.laf.intellij; +import com.intellij.ide.ui.laf.darcula.DarculaUIUtil; +import com.intellij.ide.ui.laf.darcula.ui.DarculaButtonUI; import com.intellij.util.ui.JBUI; +import javax.swing.*; import javax.swing.border.Border; import javax.swing.plaf.UIResource; import java.awt.*; +import static com.intellij.ide.ui.laf.intellij.MacIntelliJButtonUI.ARC_SIZE; + /** * @author Konstantin Bulenkov */ public class MacIntelliJButtonBorder implements Border, UIResource { @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + if (!c.hasFocus() || + c instanceof JComponent && DarculaButtonUI.isHelpButton((JComponent)c)) return; + Graphics2D g2 = (Graphics2D)g.create(); + try { + g2.translate(x, y); + DarculaUIUtil.paintFocusBorder(g2, width, height, ARC_SIZE, true); + } finally { + g2.dispose(); + } } @Override public Insets getBorderInsets(Component c) { - return JBUI.insets(2,7).asUIResource(); + return JBUI.insets(3, 3).asUIResource(); } @Override diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJButtonUI.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJButtonUI.java index a213d4fde03a..b49c7564e2fe 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJButtonUI.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJButtonUI.java @@ -16,8 +16,12 @@ package com.intellij.ide.ui.laf.intellij; import com.intellij.ide.ui.UISettings; +import com.intellij.ide.ui.laf.IntelliJLaf; import com.intellij.ide.ui.laf.darcula.ui.DarculaButtonUI; import com.intellij.ui.Gray; +import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.MacUIUtil; +import com.intellij.util.ui.UIUtil; import sun.swing.SwingUtilities2; import javax.swing.*; @@ -25,8 +29,8 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicHTML; import javax.swing.text.View; import java.awt.*; -import java.awt.geom.Area; -import java.awt.geom.Rectangle2D; +import java.awt.geom.Path2D; +import java.awt.geom.RoundRectangle2D; /** * @author Konstantin Bulenkov @@ -36,11 +40,14 @@ public class MacIntelliJButtonUI extends DarculaButtonUI { private static Rectangle textRect = new Rectangle(); private static Rectangle iconRect = new Rectangle(); + static final int ARC_SIZE = JBUI.scale(6); + @SuppressWarnings({"MethodOverridesStaticMethodOfSuperclass", "UnusedDeclaration"}) public static ComponentUI createUI(JComponent c) { return new MacIntelliJButtonUI(); } + @SuppressWarnings("UseJBColor") @Override public void paint(Graphics g, JComponent c) { if (!(c.getBorder() instanceof MacIntelliJButtonBorder) && !isComboButton(c)) { @@ -56,86 +63,87 @@ public class MacIntelliJButtonUI extends DarculaButtonUI { icon.paintIcon(c, g, x, y); } else { AbstractButton b = (AbstractButton) c; - String text = layout(b, SwingUtilities2.getFontMetrics(b, g), - b.getWidth(), b.getHeight()); + FontMetrics fm = SwingUtilities2.getFontMetrics(b, g); + String text = isSquare(c) ? layout(b, "...", null, fm, b.getWidth(), b.getHeight()) : + layout(b, b.getText(), b.getIcon(), fm, b.getWidth(), b.getHeight()); - boolean isFocused = c.hasFocus(); - if (isSquare(c)) { - Icon icon = MacIntelliJIconCache.getIcon("browseButton"); - int x = (c.getWidth() - icon.getIconWidth()) / 2; - int y = (c.getHeight() - icon.getIconHeight()) / 2; - icon.paintIcon(c, g, x, y); - return; - } else { - int x = isFocused ? 0 : 2; - int y = isFocused ? 0 : (h - viewRect.height) / 2; - Icon icon; - icon = getLeftIcon(b); - icon.paintIcon(b, g, x, y); - x += icon.getIconWidth(); - int stop = w - (isFocused ? 0 : 2) - (getRightIcon(b).getIconWidth()); + Graphics2D g2 = (Graphics2D)g.create(); + try { + g2.translate(0, 0); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, MacUIUtil.USE_QUARTZ ? RenderingHints.VALUE_STROKE_PURE : RenderingHints.VALUE_STROKE_NORMALIZE); - Graphics gg = g.create(0, 0, w, h); - Area area = new Area(new Rectangle2D.Double(x, y, stop - x, h)); - area.intersect(new Area(gg.getClip())); - gg.setClip(area); + double lw = UIUtil.isRetina(g2) ? 0.5 : 1.0; + Insets i = c.getBorder().getBorderInsets(c); // ComboButton adds arrow width to the insets, so take the bare border. - icon = getMiddleIcon(b); - while (x < stop) { - icon.paintIcon(b, gg, x, y); - x += icon.getIconWidth(); - } - gg.dispose(); - icon = getRightIcon(b); - icon.paintIcon(b, g, stop, y); - - clearTextShiftOffset(); - } - - // Paint the Icon - if(b.getIcon() != null) { - paintIcon(g,c,iconRect); - } - - if (text != null && !text.isEmpty()){ - View v = (View) c.getClientProperty(BasicHTML.propertyKey); - if (v != null) { - v.paint(g, textRect); + // Draw background + Shape outerRect = new RoundRectangle2D.Double(i.left, i.top, w - (i.left + i.right), h - (i.top + i.bottom), + ARC_SIZE, ARC_SIZE); + Paint p; + if (isDefaultButton(c)) { + p = IntelliJLaf.isGraphite() ? + new GradientPaint(w/2, i.top, new Color(0xb2b2b7), w/2, h - (i.top + i.bottom), new Color(0x929297)) : + new GradientPaint(w/2, i.top, new Color(0x84b0f7), w/2, h - (i.top + i.bottom), new Color(0x4b80fb)); + } else if (b.isEnabled()) { + p = Gray.xFF; } else { - UISettings.setupAntialiasing(g); - paintText(g, b, textRect, text); + p = Gray.xF1; + } + + g2.setPaint(p); + g2.fill(outerRect); + + // Draw outline + Path2D outline = new Path2D.Double(Path2D.WIND_EVEN_ODD); + outline.append(outerRect, false); + outline.append(new RoundRectangle2D.Double(i.left + lw, i.top + lw, + w - lw*2 - (i.left + i.right), + h - lw*2 - (i.top + i.bottom), + ARC_SIZE - lw, ARC_SIZE - lw), false); + + if (isDefaultButton(c)) { + p = IntelliJLaf.isGraphite() ? + new GradientPaint(w/2, i.top, new Color(0xa5a5ab), w/2, h - (i.top + i.bottom), new Color(0x7d7d83)) : + new GradientPaint(w/2, i.top, new Color(0x6d9ff6), w/2, h - (i.top + i.bottom), new Color(0x3861fa)); + } else if (b.isEnabled()) { + p = new GradientPaint(w/2, i.top, Gray.xC9, w/2, h - (i.top + i.bottom), Gray.xAC); + } else { + p = new GradientPaint(w/2, i.top, Gray.xD2, w/2, h - (i.top + i.bottom), Gray.xC3); + } + + g2.setPaint(p); + g2.fill(outline); + } finally { + g2.dispose(); + } + + if (isSquare(c)) { + UISettings.setupAntialiasing(g); + paintText(g, b, textRect, text); + } else { + // Paint the Icon + if(b.getIcon() != null) { + paintIcon(g,c,iconRect); + } + + if (text != null && !text.isEmpty()){ + View v = (View) c.getClientProperty(BasicHTML.propertyKey); + if (v != null) { + v.paint(g, textRect); + } else { + UISettings.setupAntialiasing(g); + paintText(g, b, textRect, text); + } } } } } - protected static boolean isComboButton(JComponent c) { + private static boolean isComboButton(JComponent c) { return c instanceof AbstractButton && c.getClientProperty("styleCombo") == Boolean.TRUE; } - private static Icon getLeftIcon(AbstractButton button) { - return getIcon("Left", button); - } - - private static Icon getMiddleIcon(AbstractButton button) { - return getIcon("Middle", button); - } - - private static Icon getRightIcon(AbstractButton button) { - return getIcon("Right", button); - } - - private static Icon getIcon(String suffix, AbstractButton button) { - boolean isDefault = isDefaultButton(button); - boolean isFocused = button.hasFocus(); - boolean combo = isComboButton(button); - String comboPrefix = combo ? "Combo" : ""; - String iconName = "button" + comboPrefix + suffix; - return MacIntelliJIconCache.getIcon(iconName, isDefault, isFocused && !combo); - } - - private String layout(AbstractButton b, FontMetrics fm, - int width, int height) { + private String layout(AbstractButton b, String text, Icon icon, FontMetrics fm, int width, int height) { Insets i = b.getInsets(); viewRect.x = i.left; viewRect.y = i.top; @@ -151,18 +159,20 @@ public class MacIntelliJButtonUI extends DarculaButtonUI { // layout the text and icon return SwingUtilities.layoutCompoundLabel( - b, fm, b.getText(), b.getIcon(), + b, fm, text, icon, b.getVerticalAlignment(), b.getHorizontalAlignment(), b.getVerticalTextPosition(), b.getHorizontalTextPosition(), - viewRect, iconRect, textRect, - b.getText() == null ? 0 : b.getIconTextGap()); + viewRect, iconRect, textRect, text == null ? 0 : b.getIconTextGap()); } @Override public Dimension getPreferredSize(JComponent c) { Dimension size = super.getPreferredSize(c); - if (c.getBorder() instanceof MacIntelliJButtonBorder || isComboButton(c)) { - return new Dimension(size.width + (isComboButton(c) ? 8 : 16), 27); + if (isHelpButton(c)) { + Icon icon = MacIntelliJIconCache.getIcon("helpButton", false, false); + return new Dimension(icon.getIconWidth(), icon.getIconHeight()); + } else if (c.getBorder() instanceof MacIntelliJButtonBorder || isComboButton(c)) { + return new Dimension(size.width + (isComboButton(c) ? 10 : 18), 27); } return size; } diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJTextBorder.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJTextBorder.java index 040e489f92c0..7649a6fb64d0 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJTextBorder.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJTextBorder.java @@ -16,14 +16,16 @@ package com.intellij.ide.ui.laf.intellij; import com.intellij.ide.ui.laf.darcula.DarculaUIUtil; -import com.intellij.ide.ui.laf.darcula.ui.DarculaTextBorder; import com.intellij.ide.ui.laf.darcula.ui.TextFieldWithPopupHandlerUI; +import com.intellij.openapi.ui.ErrorBorderCapable; import com.intellij.openapi.util.registry.Registry; import com.intellij.ui.Gray; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.plaf.UIResource; import java.awt.*; import java.awt.geom.Area; import java.awt.geom.Path2D; @@ -32,7 +34,7 @@ import java.awt.geom.Rectangle2D; /** * @author Konstantin Bulenkov */ -public class MacIntelliJTextBorder extends DarculaTextBorder { +public class MacIntelliJTextBorder implements Border, UIResource, ErrorBorderCapable { @Override public Insets getBorderInsets(Component c) { return JBUI.insets(3, 6).asUIResource(); @@ -84,7 +86,7 @@ public class MacIntelliJTextBorder extends DarculaTextBorder { } boolean isFocused(Component c) { - return c.hasFocus(); + return c instanceof JScrollPane ? ((JScrollPane)c).getViewport().getView().hasFocus() :c.hasFocus(); } void clipForBorder(Component c, Graphics2D g2, int width, int height) { diff --git a/platform/platform-impl/src/com/intellij/idea/StartupUtil.java b/platform/platform-impl/src/com/intellij/idea/StartupUtil.java index 21695914234e..9cbdcebb3653 100644 --- a/platform/platform-impl/src/com/intellij/idea/StartupUtil.java +++ b/platform/platform-impl/src/com/intellij/idea/StartupUtil.java @@ -151,8 +151,7 @@ public class StartupUtil { * Checks if the program can run under the JDK it was started with. */ private static boolean checkJdkVersion() { - String jreCheck = System.getProperty("idea.jre.check"); - if (jreCheck != null && "true".equals(jreCheck)) { + if ("true".equals(System.getProperty("idea.jre.check"))) { try { // try to find a class from tools.jar Class.forName("com.sun.jdi.Field", false, StartupUtil.class.getClassLoader()); @@ -169,18 +168,12 @@ public class StartupUtil { Main.showMessage("JDK Required", message, true); return false; } - - if (StringUtil.containsIgnoreCase(System.getProperty("java.vm.name", ""), "OpenJDK") && !SystemInfo.isJavaVersionAtLeast("1.7")) { - String message = "OpenJDK 6 is not supported. Please use Oracle Java or newer OpenJDK."; - Main.showMessage("Unsupported JVM", message, true); - return false; - } } - jreCheck = System.getProperty("idea.64bit.check"); - if (jreCheck != null && "true".equals(jreCheck)) { + + if ("true".equals(System.getProperty("idea.64bit.check"))) { if (PlatformUtils.isCidr() && !SystemInfo.is64Bit) { - String message = "32-bit JVM is not supported. Please install 64-bit version."; - Main.showMessage("Unsupported JVM", message, true); + String message = "32-bit JVM is not supported. Please install 64-bit version."; + Main.showMessage("Unsupported JVM", message, true); return false; } } diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentReferenceManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentReferenceManagerImpl.java index fb9d979bd478..f2ef500c981a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentReferenceManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentReferenceManagerImpl.java @@ -23,8 +23,8 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; +import com.intellij.openapi.vfs.VirtualFileListener; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; import com.intellij.reference.SoftReference; @@ -51,7 +51,7 @@ public class DocumentReferenceManagerImpl extends DocumentReferenceManager imple @Override public void initComponent() { - VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() { + VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileListener() { @Override public void fileCreated(@NotNull VirtualFileEvent event) { VirtualFile f = event.getFile(); diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java index b1e02f76f0d9..3baf56fb0039 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java @@ -22,7 +22,10 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.command.*; +import com.intellij.openapi.command.CommandEvent; +import com.intellij.openapi.command.CommandListener; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.command.UndoConfirmationPolicy; import com.intellij.openapi.command.undo.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; @@ -122,7 +125,7 @@ public class UndoManagerImpl extends UndoManager implements Disposable { private void runStartupActivity() { myEditorProvider = new FocusBasedCurrentEditorProvider(); - CommandListener commandListener = new CommandAdapter() { + myCommandProcessor.addCommandListener(new CommandListener() { private boolean myStarted; @Override @@ -154,8 +157,7 @@ public class UndoManagerImpl extends UndoManager implements Disposable { onCommandFinished(myProject, "", null); } } - }; - myCommandProcessor.addCommandListener(commandListener, this); + }, this); Disposer.register(this, new DocumentUndoProvider(myProject)); diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java index 6717b1d5f6aa..dae629f81254 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java @@ -78,6 +78,11 @@ public class ServiceManagerImpl implements Disposable { } } + if ("org.jetbrains.uast.UastContext".equals(descriptor.serviceInterface) && + picoContainer.getComponentInstance(descriptor.getInterface()) != null) { + return; + } + if (!Extensions.isComponentSuitableForOs(descriptor.os)) { return; } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/ToggleShowBreadcrumbsGloballyAction.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/ToggleShowBreadcrumbsGloballyAction.java new file mode 100644 index 000000000000..4d610f8250bc --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/ToggleShowBreadcrumbsGloballyAction.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.editor.actions; + +import com.intellij.ide.ui.UISettings; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; +import com.intellij.openapi.project.DumbAware; + +public class ToggleShowBreadcrumbsGloballyAction extends ToggleAction implements DumbAware { + @Override + public boolean isSelected(AnActionEvent event) { + return EditorSettingsExternalizable.getInstance().isBreadcrumbsShown(); + } + + @Override + public void setSelected(AnActionEvent event, boolean selected) { + EditorSettingsExternalizable.getInstance().setBreadcrumbsShown(selected); + UISettings.getInstance().fireUISettingsChanged(); + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/DelegateColorScheme.java b/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/DelegateColorScheme.java index cf08946d6036..f125f22561af 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/DelegateColorScheme.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/DelegateColorScheme.java @@ -40,17 +40,6 @@ public abstract class DelegateColorScheme implements EditorColorsScheme { public EditorColorsScheme getDelegate() { return myDelegate; } - - @Nullable - @Override - public Map getGeneratedTextAttributesCache() { - return myDelegate.getGeneratedTextAttributesCache(); - } - - @Override - public void dropGeneratedTextAttributesCache() { - myDelegate.dropGeneratedTextAttributesCache(); - } public void setDelegate(@NotNull EditorColorsScheme delegate) { myDelegate = delegate; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index d3905ab5211f..f2f6f014f445 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -3017,8 +3017,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi type == VERTICAL_SCROLLBAR_LEFT ? JBScrollPane.Flip.HORIZONTAL : null); - JScrollBar vsb = myScrollPane.getVerticalScrollBar(); - if (vsb != null) vsb.setOpaque(true); myScrollingModel.scrollHorizontally(currentHorOffset); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/VisibleEditorsTracker.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/VisibleEditorsTracker.java index 252654f634a6..8fbeab7943dc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/VisibleEditorsTracker.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/VisibleEditorsTracker.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,8 @@ package com.intellij.openapi.editor.impl; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.command.CommandAdapter; import com.intellij.openapi.command.CommandEvent; +import com.intellij.openapi.command.CommandListener; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorFactory; @@ -25,7 +25,7 @@ import com.intellij.openapi.editor.EditorFactory; import java.util.HashSet; import java.util.Set; -public class VisibleEditorsTracker extends CommandAdapter { +public class VisibleEditorsTracker implements CommandListener { private final Set myEditorsVisibleOnCommandStart = new HashSet<>(); private long myCurrentCommandStart; private long myLastCommandFinish; diff --git a/platform/platform-impl/src/com/intellij/openapi/fileChooser/impl/FileTreeBuilder.java b/platform/platform-impl/src/com/intellij/openapi/fileChooser/impl/FileTreeBuilder.java index 31383bb97444..a298f0cbd26c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileChooser/impl/FileTreeBuilder.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileChooser/impl/FileTreeBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,7 +48,7 @@ public class FileTreeBuilder extends AbstractTreeBuilder { initRootNode(); - VirtualFileAdapter listener = new VirtualFileAdapter() { + VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileListener() { @Override public void propertyChanged(@NotNull VirtualFilePropertyEvent event) { doUpdate(); @@ -72,8 +72,7 @@ public class FileTreeBuilder extends AbstractTreeBuilder { private void doUpdate() { queueUpdateFrom(getRootNode(), false); } - }; - VirtualFileManager.getInstance().addVirtualFileListener(listener, this); + }, this); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java index ff1cf87acb48..549770912ab9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java @@ -1642,7 +1642,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Persis /** * Closes deleted files. Closes file which are in the deleted directories. */ - private final class MyVirtualFileListener extends VirtualFileAdapter { + private final class MyVirtualFileListener implements VirtualFileListener { @Override public void beforeFileDeletion(@NotNull VirtualFileEvent e) { assertDispatchThread(); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java index 8ea635cbfb47..0bc02a8ded0b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java @@ -16,7 +16,7 @@ package com.intellij.openapi.fileEditor.impl; import com.intellij.ide.ui.UISettings; -import com.intellij.openapi.command.CommandAdapter; +import com.intellij.openapi.Disposable; import com.intellij.openapi.command.CommandEvent; import com.intellij.openapi.command.CommandListener; import com.intellij.openapi.command.CommandProcessor; @@ -44,7 +44,7 @@ import java.lang.ref.WeakReference; import java.util.*; @State(name = "IdeDocumentHistory", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) -public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements ProjectComponent, PersistentStateComponent { +public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements ProjectComponent, Disposable, PersistentStateComponent { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl"); private static final int BACK_QUEUE_LIMIT = Registry.intValue("editor.navigation.history.stack.size"); @@ -77,7 +77,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec private final Set myChangedFilesInCurrentCommand = new THashSet<>(); private boolean myCurrentCommandHasMoves; - private final CommandListener myCommandListener = new CommandAdapter() { + private final CommandListener myCommandListener = new CommandListener() { @Override public void commandStarted(CommandEvent event) { onCommandStarted(); @@ -133,7 +133,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec } }); - VirtualFileListener fileListener = new VirtualFileAdapter() { + VirtualFileListener fileListener = new VirtualFileListener() { @Override public void fileDeleted(@NotNull VirtualFileEvent event) { onFileDeleted(); @@ -238,11 +238,6 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec } } - - @Override - public final void projectClosed() { - } - @Override public final void includeCurrentCommandAsNavigation() { myCurrentCommandIsNavigation = true; @@ -549,7 +544,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec } @Override - public final void disposeComponent() { + public final void dispose() { myLastGroupId = null; } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/NonProjectFileWritingAccessProvider.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/NonProjectFileWritingAccessProvider.java index 89db50535e5f..c3270ec73d20 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/NonProjectFileWritingAccessProvider.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/NonProjectFileWritingAccessProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ package com.intellij.openapi.fileEditor.impl; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.components.impl.stores.IProjectStore; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory; import com.intellij.openapi.module.Module; @@ -71,7 +70,7 @@ public class NonProjectFileWritingAccessProvider extends WritingAccessProvider { myProject = project; if (myInitialized.compareAndSet(false, true)) { - VirtualFileManager.getInstance().addVirtualFileListener(new OurVirtualFileAdapter()); + VirtualFileManager.getInstance().addVirtualFileListener(new OurVirtualFileListener()); } } @@ -212,7 +211,7 @@ public class NonProjectFileWritingAccessProvider extends WritingAccessProvider { public enum UnlockOption {UNLOCK, UNLOCK_DIR, UNLOCK_ALL} - private static class OurVirtualFileAdapter extends VirtualFileAdapter { + private static class OurVirtualFileListener implements VirtualFileListener { @Override public void fileCreated(@NotNull VirtualFileEvent event) { unlock(event); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorComponent.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorComponent.java index 023e7dfd5013..38c0422d43f5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorComponent.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorComponent.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,8 +41,8 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; +import com.intellij.openapi.vfs.VirtualFileListener; import com.intellij.openapi.vfs.VirtualFilePropertyEvent; import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.StatusBarEx; @@ -323,7 +323,7 @@ class TextEditorComponent extends JBLoadingPanel implements DataProvider, Dispos /** * Updates "valid" property and highlighters (if necessary) */ - private final class MyVirtualFileListener extends VirtualFileAdapter{ + private final class MyVirtualFileListener implements VirtualFileListener { @Override public void propertyChanged(@NotNull final VirtualFilePropertyEvent e) { if(VirtualFile.PROP_NAME.equals(e.getPropertyName())){ diff --git a/platform/platform-impl/src/com/intellij/openapi/options/SchemeImportUtil.java b/platform/platform-impl/src/com/intellij/openapi/options/SchemeImportUtil.java index 553bb0864da7..a1efa4c0bb0e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/SchemeImportUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/SchemeImportUtil.java @@ -41,7 +41,7 @@ public class SchemeImportUtil { @Nullable VirtualFile preselect, @Nullable String description) { final Set extensions = new HashSet<>(Arrays.asList(sourceExtensions)); - FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, false, false, false) { + FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, canSelectJarFile(sourceExtensions), false, false, false) { @Override public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) { return @@ -68,12 +68,18 @@ public class SchemeImportUtil { preselectFiles = VirtualFile.EMPTY_ARRAY; } final VirtualFile[] virtualFiles = fileChooser.choose(null, preselectFiles); - //CodeStyleSchemesUIConfiguration.Util.getRecentImportFile()); if (virtualFiles.length != 1) return null; virtualFiles[0].refresh(false, false); return virtualFiles[0]; } + private static boolean canSelectJarFile(@NotNull String[] sourceExtensions) { + for (String ext : sourceExtensions) { + if ("jar".equals(ext)) return true; + } + return false; + } + @NotNull public static Element loadSchemeDom(@NotNull VirtualFile file) throws SchemeImportException { InputStream inputStream = null; @@ -85,7 +91,7 @@ public class SchemeImportUtil { return root; } catch (IOException | JDOMException e) { - throw new SchemeImportException("Can't read from" + file.getName() + ", " + e.getMessage()); + throw new SchemeImportException(); } finally { if (inputStream != null) { diff --git a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java index 1e93115d23be..50ad08b0b7e3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java @@ -33,6 +33,7 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.ModificationTracker; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.ShutDownTracker; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.AppIconScheme; import com.intellij.openapi.wm.IdeFrame; import com.intellij.openapi.wm.WindowManager; @@ -488,7 +489,7 @@ public class DumbServiceImpl extends DumbService implements Disposable, Modifica Disposer.dispose(prevTask); } - if (PowerSaveMode.isEnabled()) { + if (PowerSaveMode.isEnabled() && Registry.is("pause.indexing.in.power.save.mode")) { indicator.setText("Indexing paused during Power Save mode..."); runWhenPowerSaveModeChanges(() -> result.complete(pollTaskQueue())); completeWhenProjectClosed(result); diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/VirtualFileUrlChangeAdapter.java b/platform/platform-impl/src/com/intellij/openapi/vfs/VirtualFileUrlChangeAdapter.java index 9ed65306441e..b8014adbdcba 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/VirtualFileUrlChangeAdapter.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/VirtualFileUrlChangeAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ import org.jetbrains.annotations.NotNull; /** * @author nik */ -public abstract class VirtualFileUrlChangeAdapter extends VirtualFileAdapter { +public abstract class VirtualFileUrlChangeAdapter implements VirtualFileListener { @Override public void fileMoved(@NotNull VirtualFileMoveEvent event) { String oldUrl = event.getOldParent().getUrl() + "/" + event.getFileName(); diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemBase.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemBase.java index 0387ca2b2d58..204024bd8595 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemBase.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -72,14 +72,13 @@ public abstract class LocalFileSystemBase extends LocalFileSystem { @Override public VirtualFile findFileByIoFile(@NotNull File file) { - String path = FileUtil.toSystemIndependentName(file.getAbsolutePath()); - return findFileByPath(path); + return findFileByPath(FileUtil.toSystemIndependentName(file.getAbsolutePath())); } @NotNull protected static File convertToIOFile(@NotNull final VirtualFile file) { String path = file.getPath(); - if (StringUtil.endsWithChar(path, ':') && path.length() == 2 && (SystemInfo.isWindows || SystemInfo.isOS2)) { + if (StringUtil.endsWithChar(path, ':') && path.length() == 2 && SystemInfo.isWindows) { path += "/"; // Make 'c:' resolve to a root directory for drive c:, not the current directory on that drive } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/EncodingPanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/EncodingPanel.java index 531dd30681fe..2e0ccaf81967 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/EncodingPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/EncodingPanel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileAdapter; +import com.intellij.openapi.vfs.VirtualFileListener; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.VirtualFilePropertyEvent; import com.intellij.openapi.vfs.encoding.ChangeFileEncodingAction; @@ -142,7 +142,7 @@ public class EncodingPanel extends EditorBasedWidget implements StatusBarWidget. updateForDocument(document); } }, this); - ApplicationManager.getApplication().getMessageBus().connect(this).subscribe(VirtualFileManager.VFS_CHANGES, new BulkVirtualFileListenerAdapter(new VirtualFileAdapter() { + ApplicationManager.getApplication().getMessageBus().connect(this).subscribe(VirtualFileManager.VFS_CHANGES, new BulkVirtualFileListenerAdapter(new VirtualFileListener() { @Override public void propertyChanged(@NotNull VirtualFilePropertyEvent event) { if (VirtualFile.PROP_ENCODING.equals(event.getPropertyName())) { diff --git a/platform/platform-impl/src/com/intellij/patterns/compiler/PatternCompilerImpl.java b/platform/platform-impl/src/com/intellij/patterns/compiler/PatternCompilerImpl.java index 153f9962ee20..e533d54cfeba 100644 --- a/platform/platform-impl/src/com/intellij/patterns/compiler/PatternCompilerImpl.java +++ b/platform/platform-impl/src/com/intellij/patterns/compiler/PatternCompilerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ package com.intellij.patterns.compiler; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringHash; import com.intellij.openapi.util.text.StringUtil; @@ -83,28 +82,25 @@ public class PatternCompilerImpl implements PatternCompiler { @Override public synchronized ElementPattern compileElementPattern(final String text) { - Node node = processElementPatternText(text, new Function() { - public Node fun(final Frame frame) { - final Object[] args = frame.params.toArray(); - for (int i = 0, argsLength = args.length; i < argsLength; i++) { - args[i] = args[i] instanceof String ? myStringInterner.intern((String)args[i]) : args[i]; - } - return new Node((Node)frame.target, myStringInterner.intern(frame.methodName), args.length == 0 ? ArrayUtil.EMPTY_OBJECT_ARRAY : args); + Node node = processElementPatternText(text, frame -> { + final Object[] args = frame.params.toArray(); + for (int i = 0, argsLength = args.length; i < argsLength; i++) { + args[i] = args[i] instanceof String ? myStringInterner.intern((String)args[i]) : args[i]; } + return new Node((Node)frame.target, myStringInterner.intern(frame.methodName), args.length == 0 ? ArrayUtil.EMPTY_OBJECT_ARRAY : args); }); if (node == null) node = new Node(ERROR_NODE, text, null); return new LazyPresentablePattern<>(node); } private static Set getStaticMethods(List patternClasses) { - return new THashSet<>(ContainerUtil.concat(patternClasses, new Function>() { - public Collection fun(final Class aClass) { - return ContainerUtil.findAll(aClass.getMethods(), method -> Modifier.isStatic(method.getModifiers()) - && Modifier.isPublic(method.getModifiers()) - && !Modifier.isAbstract(method.getModifiers()) - && ElementPattern.class.isAssignableFrom(method.getReturnType())); - } - })); + return new THashSet<>(ContainerUtil.concat( + patternClasses, + aClass -> ContainerUtil.findAll(aClass.getMethods(), + method -> Modifier.isStatic(method.getModifiers()) && + Modifier.isPublic(method.getModifiers()) && + !Modifier.isAbstract(method.getModifiers()) && + ElementPattern.class.isAssignableFrom(method.getReturnType())))); } private enum State { @@ -289,7 +285,7 @@ public class PatternCompilerImpl implements PatternCompiler { try { return Integer.valueOf(s); } - catch (NumberFormatException e) {} + catch (NumberFormatException ignored) {} return s; } @@ -571,7 +567,7 @@ public class PatternCompilerImpl implements PatternCompiler { } - public class LazyPresentablePattern implements ElementPattern { + public class LazyPresentablePattern implements ElementPattern { private ElementPattern myCompiledPattern; private final Node myNode; diff --git a/platform/platform-impl/src/com/intellij/ui/popup/StackingPopupDispatcherImpl.java b/platform/platform-impl/src/com/intellij/ui/popup/StackingPopupDispatcherImpl.java index ca88f58dfc85..093247f4b651 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/StackingPopupDispatcherImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/StackingPopupDispatcherImpl.java @@ -203,7 +203,12 @@ public class StackingPopupDispatcherImpl extends StackingPopupDispatcher impleme @Override public boolean close() { - return closeActivePopup(); + if (!closeActivePopup()) return false; + // try to close other popups in the stack + while (closeActivePopup()) { + // close all popups one by one + } + return true; // at least one popup was closed } @Override diff --git a/platform/platform-impl/src/com/intellij/ui/tree/AsyncTreeModel.java b/platform/platform-impl/src/com/intellij/ui/tree/AsyncTreeModel.java index 9e2816b20a78..df4066ed261e 100644 --- a/platform/platform-impl/src/com/intellij/ui/tree/AsyncTreeModel.java +++ b/platform/platform-impl/src/com/intellij/ui/tree/AsyncTreeModel.java @@ -38,6 +38,7 @@ import javax.swing.tree.TreePath; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableList; @@ -137,15 +138,68 @@ public final class AsyncTreeModel extends AbstractTreeModel implements Disposabl return model instanceof Navigatable ? resolve(((Navigatable)model).prevTreePath(path, object)) : rejectedPromise(); } - private Promise resolve(Promise promise) { + @NotNull + public Promise resolve(TreePath path) { AsyncPromise async = new AsyncPromise<>(); - promise.rejected(error -> processor.foreground.invokeLaterIfNeeded(() -> async.setError(error))); - promise.done(result -> processor.foreground.invokeLaterIfNeeded(() -> sync(async, result))); + processor.foreground.invokeLaterIfNeeded(() -> resolve(async, path, entry -> async.setResult(entry))); return async; } - private void sync(AsyncPromise promise, TreePath path) { - promise.setResult(path);//todo load + private Promise resolve(Promise promise) { + AsyncPromise async = new AsyncPromise<>(); + promise.rejected(error -> processor.foreground.invokeLaterIfNeeded(() -> async.setError(error))); + promise.done(result -> processor.foreground.invokeLaterIfNeeded(() -> resolve(async, result, entry -> async.setResult(entry)))); + return async; + } + + private void resolve(AsyncPromise async, TreePath path, Consumer> consumer) { + if (path == null) { + async.setError("path is null"); + return; + } + Object object = path.getLastPathComponent(); + if (object == null) { + async.setError("path is wrong"); + return; + } + if (!consume(consumer, tree.findEntry(object))) { + TreePath parent = path.getParentPath(); + if (parent == null) { + promiseRootEntry().done(entry -> { + if (entry == null) { + async.setError("root is null"); + } + else if (object != entry.getNode()) { + async.setError("root is wrong"); + } + else { + consumer.accept(entry); + } + }); + } + else { + resolve(async, parent, entry -> processor.process(new Command>>() { + private CmdGetChildren command = new CmdGetChildren("Sync children", entry, false); + + @Override + public List> get() { + return command.get(); + } + + @Override + public void accept(List> children) { + command.accept(children); + if (!consume(consumer, tree.findEntry(object))) async.setError("path not found"); + } + })); + } + } + } + + private static boolean consume(Consumer> consumer, Entry entry) { + if (entry == null) return false; + consumer.accept(entry); + return true; } @Override @@ -278,17 +332,26 @@ public final class AsyncTreeModel extends AbstractTreeModel implements Disposabl Object object = entry.getNode(); if (model.isLeaf(object)) return null; + if (model instanceof ChildrenProvider) { + //noinspection unchecked + ChildrenProvider provider = (ChildrenProvider)model; + ArrayList> children = new ArrayList<>(); + provider.getChildren(object).forEach(child -> add(children, child)); + return unmodifiableList(children); + } + int count = model.getChildCount(object); if (count <= 0) return emptyList(); ArrayList> children = new ArrayList<>(count); - for (int i = 0; i < count; i++) { - Object child = model.getChild(object, i); - children.add(Pair.create(child, model.isLeaf(child))); - } + for (int i = 0; i < count; i++) add(children, model.getChild(object, i)); return unmodifiableList(children); } + private void add(List> children, Object child) { + if (child != null) children.add(Pair.create(child, model.isLeaf(child))); + } + @Override public void accept(List> children) { Object object = entry.getNode(); diff --git a/platform/platform-impl/src/com/intellij/ui/tree/ChildrenProvider.java b/platform/platform-impl/src/com/intellij/ui/tree/ChildrenProvider.java new file mode 100644 index 000000000000..04d643f1a1d2 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/tree/ChildrenProvider.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.ui.tree; + +import org.jetbrains.annotations.NotNull; + +import javax.swing.tree.TreeModel; +import java.util.List; + +/** + * This is an extension for the {@link TreeModel} which is supported by {@link AsyncTreeModel}. + * It is intended to simplify implementing of a couple corresponding methods in a model. + * + * @see TreeModel#getChildCount(Object) + * @see TreeModel#getChild(Object, int) + * + * @author Sergey.Malenkov + */ +public interface ChildrenProvider { + /** + * @param parent a tree node + * @return all children of the specified parent node + */ + @NotNull + List getChildren(Object parent); +} diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index a756aa3b39f0..cad70ba37325 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -1499,6 +1499,7 @@ action.EditorGutterToggleGlobalSoftWraps.text=Soft-Wrap All Files action.EditorGutterToggleLocalSoftWraps.gutterText=Soft-Wrap Current File action.EditorGutterToggleGlobalLineNumbers.text=Show Line Numbers action.EditorGutterToggleGlobalIndentLines.text=Show Indent Guides +action.EditorGutterToggleGlobalBreadcrumbs.text=Show Breadcrumbs action.ExcludeFromProject.text=Exclude From Project... group.MarkFileAs.text=Mark File as action.MarkAsPlainTextAction.text=Mark as Plain Text diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index e23c2cba26ec..57ce58e9a482 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -165,6 +165,7 @@ wrapping.brace.placement=Braces placement wrapping.brace.placement.class.declaration=In class declaration wrapping.brace.placement.method.declaration=In method declaration wrapping.brace.placement.other=Other +wrapping.brace.placement.lambda=In lambda declaration wrapping.brace.placement.end.of.line=End of line wrapping.brace.placement.next.line.if.wrapped=Next line if wrapped wrapping.brace.placement.next.line=Next line diff --git a/platform/platform-resources-en/src/messages/IdeBundle.properties b/platform/platform-resources-en/src/messages/IdeBundle.properties index 207916075ac3..ba9e25bac1de 100644 --- a/platform/platform-resources-en/src/messages/IdeBundle.properties +++ b/platform/platform-resources-en/src/messages/IdeBundle.properties @@ -449,7 +449,7 @@ command.find.next=Find Next command.find.previous=Find Previous command.select.all=Select All message.no.targets.available=No targets available in this context -title.popup.select.target=Select Target +title.popup.select.target=Select In title.popup.recent.files=Recent Files action.split.vertically=Split _Vertically action.split.horizontally=Split Hori_zontally diff --git a/platform/platform-resources/src/DefaultColorSchemesManager.xml b/platform/platform-resources/src/DefaultColorSchemesManager.xml index c49a9f22b28f..0cddc35dc6e1 100644 --- a/platform/platform-resources/src/DefaultColorSchemesManager.xml +++ b/platform/platform-resources/src/DefaultColorSchemesManager.xml @@ -1039,34 +1039,23 @@ @@ -2158,33 +2147,22 @@ diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index 869ac784b8c4..e31422fb1abd 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -904,6 +904,8 @@ + diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index 524a7765bf23..65ee0752c583 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -997,7 +997,7 @@ schemeClass="com.intellij.psi.codeStyle.CodeStyleScheme" implementationClass="com.intellij.psi.impl.source.codeStyle.CodeStyleSchemeXmlImporter"/> diff --git a/platform/platform-resources/src/idea/PlatformActions.xml b/platform/platform-resources/src/idea/PlatformActions.xml index 32f974feeb4e..8f6cde011eac 100644 --- a/platform/platform-resources/src/idea/PlatformActions.xml +++ b/platform/platform-resources/src/idea/PlatformActions.xml @@ -654,6 +654,7 @@ + diff --git a/platform/platform-tests/testSrc/com/intellij/execution/impl/ConsoleViewImplTest.java b/platform/platform-tests/testSrc/com/intellij/execution/impl/ConsoleViewImplTest.java index 8ba659972832..1d93ebb40354 100644 --- a/platform/platform-tests/testSrc/com/intellij/execution/impl/ConsoleViewImplTest.java +++ b/platform/platform-tests/testSrc/com/intellij/execution/impl/ConsoleViewImplTest.java @@ -374,4 +374,14 @@ public class ConsoleViewImplTest extends LightPlatformTestCase { () -> EditorTestUtil.executeAction(editor, true, handler), "", null, editor.getDocument()); } + + public void testCRPrintCR() throws Exception { + for (int i=0;i<25;i++) { + myConsole.print("\r"+i, ConsoleViewContentType.NORMAL_OUTPUT); + Thread.sleep(100); + } + myConsole.flushDeferredText(); + myConsole.waitAllRequests(); + assertEquals("24", myConsole.getText()); + } } diff --git a/platform/platform-tests/testSrc/com/intellij/history/integration/ExternalChangesAndRefreshingTest.java b/platform/platform-tests/testSrc/com/intellij/history/integration/ExternalChangesAndRefreshingTest.java index 05a11753328d..1ff19eabaa4b 100644 --- a/platform/platform-tests/testSrc/com/intellij/history/integration/ExternalChangesAndRefreshingTest.java +++ b/platform/platform-tests/testSrc/com/intellij/history/integration/ExternalChangesAndRefreshingTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -106,7 +106,7 @@ public class ExternalChangesAndRefreshingTest extends IntegrationTestCase { public void testCommandDuringRefresh() throws Exception { createFileExternally("f.txt"); - VirtualFileListener l = new VirtualFileAdapter() { + VirtualFileListener l = new VirtualFileListener() { @Override public void fileCreated(@NotNull VirtualFileEvent e) { executeSomeCommand(); @@ -127,7 +127,7 @@ public class ExternalChangesAndRefreshingTest extends IntegrationTestCase { setContentExternally(path, "content"); final String[] content = new String[1]; - VirtualFileListener l = new VirtualFileAdapter() { + VirtualFileListener l = new VirtualFileListener() { @Override public void fileCreated(@NotNull VirtualFileEvent e) { try { diff --git a/platform/platform-tests/testSrc/com/intellij/history/integration/FileListeningTest.java b/platform/platform-tests/testSrc/com/intellij/history/integration/FileListeningTest.java index 5476e15e21d2..4767ab53ce3c 100644 --- a/platform/platform-tests/testSrc/com/intellij/history/integration/FileListeningTest.java +++ b/platform/platform-tests/testSrc/com/intellij/history/integration/FileListeningTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,10 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.util.ThrowableComputable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.*; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileListener; +import com.intellij.openapi.vfs.VirtualFilePropertyEvent; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; @@ -138,7 +141,7 @@ public class FileListeningTest extends IntegrationTestCase { final VirtualFile f = createFile("old.txt"); final int[] log = new int[2]; - VirtualFileListener l = new VirtualFileAdapter() { + VirtualFileListener l = new VirtualFileListener() { @Override public void beforePropertyChange(@NotNull VirtualFilePropertyEvent e) { log[0] = getRevisionsFor(f).size(); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/EditorColorPaletteTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/EditorColorPaletteTest.java index 5dc8c74f1e1a..dc1d6404f223 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/EditorColorPaletteTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/EditorColorPaletteTest.java @@ -55,20 +55,34 @@ public class EditorColorPaletteTest extends LightPlatformTestCase { final TextAttributes newColorAttrInPallete = new TextAttributes(); newColorAttrInPallete.setForegroundColor(newColorInPalette); + // rainbow color generator can reuse LOCAL_VARIABLE or PARAM foreground color colorsScheme.setAttributes(DefaultLanguageHighlighterColors.LOCAL_VARIABLE, newColorAttrInPallete); checkUsed(colorsScheme, newColorInPalette, true); - checkUsed(colorsScheme, colorsScheme.getAttributes(DefaultLanguageHighlighterColors.BLOCK_COMMENT).getForegroundColor(), false); + // rainbow color generator can not reuse any other foreground color + final TextAttributes bcOrigAttr = colorsScheme.getAttributes(DefaultLanguageHighlighterColors.BLOCK_COMMENT); + checkUsed(colorsScheme, bcOrigAttr.getForegroundColor(), false); + + final TextAttributesKey generatedRainbowKey = new RainbowHighlighter(null).getRainbowTempKeys()[0]; // make BLOCK_COMMENT the same as LOCAL_VARIABLE => has conflict with non-rainbow BLOCK_COMMENT color colorsScheme.setAttributes(DefaultLanguageHighlighterColors.BLOCK_COMMENT, newColorAttrInPallete); checkUsed(colorsScheme, newColorInPalette, false); + // rainbow generated colors are cached: + assertNotNull(colorsScheme.getAttributes(generatedRainbowKey)); + + // chage in colorsScheme for non-mutable key drops generated cache + colorsScheme.setAttributes(DefaultLanguageHighlighterColors.BLOCK_COMMENT, bcOrigAttr); + // there are no rainbow generated colors: + assertNull(colorsScheme.getAttributes(generatedRainbowKey)); + // resolve conflict for BLACK color (checking overflow) final TextAttributes blackColorAttrInPallete = new TextAttributes(); blackColorAttrInPallete.setForegroundColor(Color.BLACK); colorsScheme.setAttributes(DefaultLanguageHighlighterColors.BLOCK_COMMENT, blackColorAttrInPallete); checkUsed(colorsScheme, Color.BLACK, false); + // resolve conflict for WHITE color (checking overflow) final TextAttributes whiteColorAttrInPallete = new TextAttributes(); whiteColorAttrInPallete.setForegroundColor(Color.WHITE); colorsScheme.setAttributes(DefaultLanguageHighlighterColors.BLOCK_COMMENT, whiteColorAttrInPallete); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/DummyFileSystemTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/DummyFileSystemTest.java index 2477af88141c..c6379fb8ac28 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/DummyFileSystemTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/DummyFileSystemTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,7 +47,7 @@ public class DummyFileSystemTest extends BareTestFixtureTestCase { VirtualFileEvent[] events = new VirtualFileEvent[2]; - VirtualFileAdapter listener = new VirtualFileAdapter() { + VirtualFileListener listener = new VirtualFileListener() { @Override public void beforeFileDeletion(@NotNull VirtualFileEvent e) { events[0] = e; diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VirtualFileListenerTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VirtualFileListenerTest.java index e6538f664aff..b8225fcb5ab1 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VirtualFileListenerTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VirtualFileListenerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,7 +43,7 @@ public class VirtualFileListenerTest extends BareTestFixtureTestCase { Ref eventFired = Ref.create(false); - VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() { + VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileListener() { @Override public void fileCreated(@NotNull VirtualFileEvent event) { eventFired.set(true); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java index 760a77b8bb94..211fe67ed447 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java @@ -665,7 +665,7 @@ public class VirtualFilePointerTest extends PlatformTestCase { assertNotNull(virtualFile); assertTrue(virtualFile.isValid()); final Collection> reads = ContainerUtil.newConcurrentSet(); - VirtualFileAdapter listener = new VirtualFileAdapter() { + VirtualFileListener listener = new VirtualFileListener() { @Override public void fileCreated(@NotNull VirtualFileEvent event) { stressRead(pointer, reads); @@ -857,7 +857,7 @@ public class VirtualFilePointerTest extends PlatformTestCase { PsiTestUtil.addLibrary(getModule(), dir1.getPath()); - VirtualFileAdapter listener = new VirtualFileAdapter() { + VirtualFileListener listener = new VirtualFileListener() { @Override public void fileDeleted(@NotNull VirtualFileEvent event) { ProjectRootManager.getInstance(getProject()).getFileIndex().getModuleForFile(dir1); diff --git a/platform/platform-tests/testSrc/org/jetbrains/concurrency/AsyncPromiseTest.kt b/platform/platform-tests/testSrc/org/jetbrains/concurrency/AsyncPromiseTest.kt index c7c7841b9bf7..3e28d71918c8 100644 --- a/platform/platform-tests/testSrc/org/jetbrains/concurrency/AsyncPromiseTest.kt +++ b/platform/platform-tests/testSrc/org/jetbrains/concurrency/AsyncPromiseTest.kt @@ -19,6 +19,7 @@ import com.intellij.testFramework.assertConcurrent import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test +import java.util.concurrent.TimeUnit import java.util.concurrent.TimeoutException import java.util.concurrent.atomic.AtomicInteger @@ -48,7 +49,7 @@ class AsyncPromiseTest { val numThreads = 30 assertConcurrent(*Array(numThreads, { - if (it and 1 === 0) r else s + if ((it and 1) == 0) r else s })) assertThat(count.get()).isEqualTo(numThreads / 2) @@ -69,6 +70,13 @@ class AsyncPromiseTest { }) } + @Test + fun `ignoreErrors`() { + val a = resolvedPromise("foo") + val b = rejectedPromise() + assertThat(collectResults(listOf(a, b), ignoreErrors = true).blockingGet(100, TimeUnit.MILLISECONDS)).containsExactly("foo") + } + @Test fun blockingGet2() { val promise = AsyncPromise() diff --git a/platform/projectModel-api/src/org/jetbrains/concurrency/AsyncPromise.kt b/platform/projectModel-api/src/org/jetbrains/concurrency/AsyncPromise.kt index b6312da03294..9153b11c70f7 100644 --- a/platform/projectModel-api/src/org/jetbrains/concurrency/AsyncPromise.kt +++ b/platform/projectModel-api/src/org/jetbrains/concurrency/AsyncPromise.kt @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -78,6 +78,10 @@ open class AsyncPromise : Promise, Getter { override fun notify(child: AsyncPromise) { LOG.assertTrue(child !== this) + if (child.state != State.PENDING) { + return + } + when (state) { State.PENDING -> { addHandlers(Consumer({ child.catchError { child.setResult(it) } }), Consumer({ child.setError(it) })) diff --git a/platform/projectModel-api/src/org/jetbrains/concurrency/promise.kt b/platform/projectModel-api/src/org/jetbrains/concurrency/promise.kt index 56851f141d5f..1cedd059c493 100644 --- a/platform/projectModel-api/src/org/jetbrains/concurrency/promise.kt +++ b/platform/projectModel-api/src/org/jetbrains/concurrency/promise.kt @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -116,7 +116,8 @@ inline fun Promise<*>.rejected(node: Obsolescent, crossinline handler: (Throwabl override fun consume(param: Throwable) = handler(param) }) -fun collectResults(promises: List>): Promise> { +@JvmOverloads +fun collectResults(promises: List>, ignoreErrors: Boolean = false): Promise> { if (promises.isEmpty()) { return resolvedPromise(emptyList()) } @@ -125,7 +126,7 @@ fun collectResults(promises: List>): Promise> { for (promise in promises) { promise.done { results.add(it) } } - return all(promises, results) + return all(promises, results, ignoreErrors) } @JvmOverloads @@ -187,7 +188,8 @@ fun ActionCallback.toPromise(): Promise { fun all(promises: Collection>): Promise<*> = if (promises.size == 1) promises.first() else all(promises, null) -fun all(promises: Collection>, totalResult: T?): Promise { +@JvmOverloads +fun all(promises: Collection>, totalResult: T?, ignoreErrors: Boolean = false): Promise { if (promises.isEmpty()) { @Suppress("UNCHECKED_CAST") return DONE as Promise @@ -195,7 +197,7 @@ fun all(promises: Collection>, totalResult: T?): Promise { val totalPromise = AsyncPromise() val done = CountDownConsumer(promises.size, totalPromise, totalResult) - val rejected = Consumer { error -> totalPromise.setError(error) } + val rejected = if (ignoreErrors) Consumer { done.consume(null) } else Consumer { totalPromise.setError(it) } for (promise in promises) { promise.done(done) diff --git a/platform/remote-servers/impl/src/com/intellij/remoteServer/impl/runtime/ui/ServersToolWindowContent.java b/platform/remote-servers/impl/src/com/intellij/remoteServer/impl/runtime/ui/ServersToolWindowContent.java index 40dc2f710abb..b5ba3bd67ed4 100644 --- a/platform/remote-servers/impl/src/com/intellij/remoteServer/impl/runtime/ui/ServersToolWindowContent.java +++ b/platform/remote-servers/impl/src/com/intellij/remoteServer/impl/runtime/ui/ServersToolWindowContent.java @@ -14,6 +14,8 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; +import com.intellij.remoteServer.configuration.RemoteServer; +import com.intellij.remoteServer.configuration.RemoteServerListener; import com.intellij.remoteServer.impl.runtime.ui.tree.ServersTreeNodeSelector; import com.intellij.remoteServer.impl.runtime.ui.tree.ServersTreeStructure; import com.intellij.remoteServer.impl.runtime.ui.tree.TreeBuilderBase; @@ -241,6 +243,18 @@ public class ServersToolWindowContent extends JPanel implements Disposable, Serv updateSelectedServerDetails(); } }); + + project.getMessageBus().connect().subscribe(RemoteServerListener.TOPIC, new RemoteServerListener() { + @Override + public void serverAdded(@NotNull RemoteServer server) { + getBuilder().queueUpdate(); + } + + @Override + public void serverRemoved(@NotNull RemoteServer server) { + getBuilder().queueUpdate(); + } + }); } private void updateSelectedServerDetails() { @@ -312,12 +326,10 @@ public class ServersToolWindowContent extends JPanel implements Disposable, Serv } public void select(@NotNull final ServerConnection connection, @NotNull final String deploymentName) { - myBuilder.getUi().queueUpdate(connection).doWhenDone(() -> myBuilder.select(ServersTreeStructure.DeploymentNodeImpl.class, new TreeVisitor() { - @Override - public boolean visit(@NotNull ServersTreeStructure.DeploymentNodeImpl node) { - return isDeploymentNodeMatch(node, connection, deploymentName); - } - }, null, false)); + myBuilder.getUi().queueUpdate(connection).doWhenDone( + () -> myBuilder.select(ServersTreeStructure.DeploymentNodeImpl.class, + node -> isDeploymentNodeMatch(node, connection, deploymentName), + null, false)); } public void select(@NotNull final ServerConnection connection, diff --git a/platform/testFramework/bootstrap/src/com/intellij/tests/ExternalClasspathClassLoader.java b/platform/testFramework/bootstrap/src/com/intellij/tests/ExternalClasspathClassLoader.java index 18599b74967e..ab95ffe3f1ae 100644 --- a/platform/testFramework/bootstrap/src/com/intellij/tests/ExternalClasspathClassLoader.java +++ b/platform/testFramework/bootstrap/src/com/intellij/tests/ExternalClasspathClassLoader.java @@ -22,49 +22,45 @@ import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import java.util.Collection; -import java.util.LinkedHashSet; +import java.util.*; /** * @author max */ public class ExternalClasspathClassLoader { - private static String[] parseUrls(String classpathFilePath) { - Collection roots = new LinkedHashSet<>(); + private static List loadFilesPaths(String classpathFilePath) { + Set roots = new LinkedHashSet<>(); File file = new File(classpathFilePath); try { final BufferedReader reader = new BufferedReader(new FileReader(file)); try { while (reader.ready()) { - roots.add(reader.readLine()); + roots.add(new File(reader.readLine())); } } finally { reader.close(); } - - //noinspection SSBasedInspection - return roots.toArray(new String[roots.size()]); + return new ArrayList<>(roots); } catch (IOException e) { throw new RuntimeException(e); } } - public static String[] getRoots() { + public static List getRoots() { final String classPathFilePath = System.getProperty("classpath.file"); - return classPathFilePath != null ? parseUrls(classPathFilePath) : null; + return classPathFilePath != null ? loadFilesPaths(classPathFilePath) : null; } - public static String[] getExcludeRoots() { + public static List getExcludeRoots() { try { final String classPathFilePath = System.getProperty("exclude.tests.roots.file"); - return classPathFilePath != null ? parseUrls(classPathFilePath) : null; + return classPathFilePath != null ? loadFilesPaths(classPathFilePath) : null; } catch (Exception e) { - //noinspection SSBasedInspection - return new String[0]; + return Collections.emptyList(); } } @@ -94,12 +90,12 @@ public class ExternalClasspathClassLoader { private static URL[] parseUrls() { try { - String[] roots = getRoots(); + List roots = getRoots(); if (roots == null) return null; - URL[] urls = new URL[roots.length]; + URL[] urls = new URL[roots.size()]; for (int i = 0; i < urls.length; i++) { - urls[i] = new File(roots[i]).toURI().toURL(); + urls[i] = roots.get(i).toURI().toURL(); } return urls; } diff --git a/platform/testFramework/src/com/intellij/ClassFinder.java b/platform/testFramework/src/com/intellij/ClassFinder.java index 33305fa2bbef..cebe29ea7172 100644 --- a/platform/testFramework/src/com/intellij/ClassFinder.java +++ b/platform/testFramework/src/com/intellij/ClassFinder.java @@ -42,10 +42,10 @@ public class ClassFinder { private final int startPackageName; private final boolean includeUnconventionallyNamedTests; - public ClassFinder(final File classPathRoot, final String packageRoot, boolean includeUnconventionallyNamedTests) throws IOException { + public ClassFinder(final File classPathRoot, final String rootPackage, boolean includeUnconventionallyNamedTests) throws IOException { this.includeUnconventionallyNamedTests = includeUnconventionallyNamedTests; startPackageName = classPathRoot.getAbsolutePath().length() + 1; - String directoryOffset = packageRoot.replace('.', File.separatorChar); + String directoryOffset = rootPackage.replace('.', File.separatorChar); findAndStoreTestClasses(new File(classPathRoot, directoryOffset)); } diff --git a/platform/testFramework/src/com/intellij/TestAll.java b/platform/testFramework/src/com/intellij/TestAll.java index f9a9a8adee10..52e76e6e071d 100644 --- a/platform/testFramework/src/com/intellij/TestAll.java +++ b/platform/testFramework/src/com/intellij/TestAll.java @@ -27,14 +27,15 @@ package com.intellij; import com.intellij.idea.Bombed; import com.intellij.idea.RecordExecution; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.testFramework.*; import com.intellij.tests.ExternalClasspathClassLoader; import com.intellij.util.ArrayUtil; import com.intellij.util.ReflectionUtil; +import gnu.trove.THashSet; import junit.framework.*; +import one.util.streamex.StreamEx; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.runner.Description; @@ -48,7 +49,8 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; import java.net.URLClassLoader; -import java.util.*; +import java.util.ArrayList; +import java.util.List; @SuppressWarnings({"HardCodedStringLiteral", "CallToPrintStackTrace", "UseOfSystemOutOrSystemErr", "TestOnlyProblems", "BusyWait"}) public class TestAll implements Test { @@ -108,20 +110,20 @@ public class TestAll implements Test { private static final List outClassLoadingProblems = new ArrayList<>(); - public TestAll(String packageRoot) throws Throwable { - this(packageRoot, getClassRoots()); + public TestAll(String rootPackage) throws Throwable { + this(rootPackage, getClassRoots()); } - public TestAll(String packageRoot, String... classRoots) throws IOException, ClassNotFoundException { + public TestAll(String rootPackage, List classesRoots) throws IOException, ClassNotFoundException { String classFilterName = "tests/testGroups.properties"; - if (Boolean.getBoolean("idea.ignore.predefined.groups") || (ourMode & FILTER_CLASSES) == 0) { + if ((ourMode & FILTER_CLASSES) == 0) { classFilterName = ""; } myTestCaseLoader = new TestCaseLoader(classFilterName); myTestCaseLoader.addFirstTest(Class.forName("_FirstInSuiteTest")); myTestCaseLoader.addLastTest(Class.forName("_LastInSuiteTest")); - fillTestCases(myTestCaseLoader, packageRoot, classRoots); + fillTestCases(myTestCaseLoader, rootPackage, classesRoots); outClassLoadingProblems.addAll(myTestCaseLoader.getClassLoadingErrors()); } @@ -130,22 +132,22 @@ public class TestAll implements Test { return outClassLoadingProblems; } - public static String[] getClassRoots() { + public static List getClassRoots() { String testRoots = System.getProperty("test.roots"); if (testRoots != null) { System.out.println("Collecting tests from roots specified by test.roots property: " + testRoots); - return testRoots.split(";"); + return StreamEx.of(testRoots.split(";")).map(File::new).toList(); } - String[] roots = ExternalClasspathClassLoader.getRoots(); + List roots = ExternalClasspathClassLoader.getRoots(); if (roots != null) { - if (Comparing.equal(System.getProperty(TestCaseLoader.SKIP_COMMUNITY_TESTS), "true")) { - System.out.println("Skipping community tests"); - Set set = normalizePaths(roots); - set.removeAll(normalizePaths(ExternalClasspathClassLoader.getExcludeRoots())); - roots = ArrayUtil.toStringArray(set); + List excludeRoots = ExternalClasspathClassLoader.getExcludeRoots(); + if (excludeRoots != null) { + System.out.println("Skipping tests from " + excludeRoots.size() + " roots"); + roots = new ArrayList<>(roots); + roots.removeAll(new THashSet<>(excludeRoots, FileUtil.FILE_HASHING_STRATEGY)); } - System.out.println("Collecting tests from roots specified by classpath.file property: " + Arrays.toString(roots)); + System.out.println("Collecting tests from roots specified by classpath.file property: " + roots); return roots; } else { @@ -162,37 +164,25 @@ public class TestAll implements Test { } catch (Throwable ignore) {} } - return System.getProperty("java.class.path").split(File.pathSeparator); + return StreamEx.of(System.getProperty("java.class.path").split(File.pathSeparator)).map(File::new).toList(); } } - private static String[] getClassRoots(URL[] urls) { - final String[] classLoaderRoots = new String[urls.length]; - for (int i = 0; i < urls.length; i++) { - classLoaderRoots[i] = VfsUtilCore.urlToPath(VfsUtilCore.convertFromUrl(urls[i])); - } - System.out.println("Collecting tests from " + Arrays.toString(classLoaderRoots)); + private static List getClassRoots(URL[] urls) { + final List classLoaderRoots = StreamEx.of(urls).map(url -> new File(VfsUtilCore.urlToPath(VfsUtilCore.convertFromUrl(url)))).toList(); + System.out.println("Collecting tests from " + classLoaderRoots); return classLoaderRoots; } - private static Set normalizePaths(String[] array) { - Set answer = new LinkedHashSet<>(array.length); - for (String path : array) { - answer.add(path.replace('\\', '/')); - } - return answer; - } - - public static void fillTestCases(TestCaseLoader testCaseLoader, String packageRoot, String... classRoots) throws IOException { + public static void fillTestCases(TestCaseLoader testCaseLoader, String rootPackage, List classesRoots) throws IOException { long before = System.currentTimeMillis(); - for (String classRoot : classRoots) { + for (File classesRoot : classesRoots) { int oldCount = testCaseLoader.getClasses().size(); - File classRootFile = new File(FileUtil.toSystemDependentName(classRoot)); - ClassFinder classFinder = new ClassFinder(classRootFile, packageRoot, INCLUDE_UNCONVENTIONALLY_NAMED_TESTS); - testCaseLoader.loadTestCases(classRootFile.getName(), classFinder.getClasses()); + ClassFinder classFinder = new ClassFinder(classesRoot, rootPackage, INCLUDE_UNCONVENTIONALLY_NAMED_TESTS); + testCaseLoader.loadTestCases(classesRoot.getName(), classFinder.getClasses()); int newCount = testCaseLoader.getClasses().size(); if (newCount != oldCount) { - System.out.println("Loaded " + (newCount - oldCount) + " tests from class root " + classRoot); + System.out.println("Loaded " + (newCount - oldCount) + " tests from class root " + classesRoot); } } diff --git a/platform/testFramework/src/com/intellij/TestCaseLoader.java b/platform/testFramework/src/com/intellij/TestCaseLoader.java index ed0e363dd55c..0f1a20d6f98b 100644 --- a/platform/testFramework/src/com/intellij/TestCaseLoader.java +++ b/platform/testFramework/src/com/intellij/TestCaseLoader.java @@ -50,7 +50,6 @@ public class TestCaseLoader { public static final String PERFORMANCE_TESTS_ONLY_FLAG = "idea.performance.tests"; public static final String INCLUDE_PERFORMANCE_TESTS_FLAG = "idea.include.performance.tests"; public static final String INCLUDE_UNCONVENTIONALLY_NAMED_TESTS_FLAG = "idea.include.unconventionally.named.tests"; - public static final String SKIP_COMMUNITY_TESTS = "idea.skip.community.tests"; /** * An implicit group which includes all tests from all defined groups and tests which don't belong to any group. diff --git a/platform/testFramework/src/com/intellij/testFramework/LightProjectDescriptor.java b/platform/testFramework/src/com/intellij/testFramework/LightProjectDescriptor.java index 6edbba415677..bb5dba424cc7 100644 --- a/platform/testFramework/src/com/intellij/testFramework/LightProjectDescriptor.java +++ b/platform/testFramework/src/com/intellij/testFramework/LightProjectDescriptor.java @@ -46,7 +46,7 @@ import java.io.IOException; public class LightProjectDescriptor { public static final LightProjectDescriptor EMPTY_PROJECT_DESCRIPTOR = new LightProjectDescriptor(); - protected static final String TEST_MODULE_NAME = "light_idea_test_case"; + public static final String TEST_MODULE_NAME = "light_idea_test_case"; public void setUpProject(@NotNull Project project, @NotNull SetupHandler handler) throws Exception { WriteAction.run(() -> { diff --git a/community-tests/src/tests/testGroups.properties b/platform/testFramework/testSrc/tests/testGroups.properties similarity index 100% rename from community-tests/src/tests/testGroups.properties rename to platform/testFramework/testSrc/tests/testGroups.properties diff --git a/platform/util-rt/src/com/intellij/openapi/util/SystemInfoRt.java b/platform/util-rt/src/com/intellij/openapi/util/SystemInfoRt.java index 3ed08ab5f207..beba4e3bb270 100644 --- a/platform/util-rt/src/com/intellij/openapi/util/SystemInfoRt.java +++ b/platform/util-rt/src/com/intellij/openapi/util/SystemInfoRt.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,13 +29,17 @@ public class SystemInfoRt { private static final String _OS_NAME = OS_NAME.toLowerCase(Locale.US); public static final boolean isWindows = _OS_NAME.startsWith("windows"); - public static final boolean isOS2 = _OS_NAME.startsWith("os/2") || _OS_NAME.startsWith("os2"); public static final boolean isMac = _OS_NAME.startsWith("mac"); public static final boolean isLinux = _OS_NAME.startsWith("linux"); public static final boolean isFreeBSD = _OS_NAME.startsWith("freebsd"); public static final boolean isSolaris = _OS_NAME.startsWith("sunos"); - public static final boolean isUnix = !isWindows && !isOS2; + public static final boolean isUnix = !isWindows; public static final boolean isFileSystemCaseSensitive = isUnix && !isMac || "true".equalsIgnoreCase(System.getProperty("idea.case.sensitive.fs")); + + // + /** @deprecated outdated (to be removed in IDEA 2018) */ + public static final boolean isOS2 = _OS_NAME.startsWith("os/2") || _OS_NAME.startsWith("os2"); + // } \ No newline at end of file diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index fd65762d4486..055af47b39e6 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -930,9 +930,6 @@ editor.breadcrumbs.highlight.on.hover.description=Highlight corresponding ranges editor.breadcrumbs.above=false editor.breadcrumbs.above.description=Show breadcrumbs above or below an editor -editor.breadcrumbs.alpha=0.6 -editor.breadcrumbs.alpha.description=Temporary key for designers to tune a color - testDiscovery.enabled=false testDiscovery.enabled.description=Enable instrumentation during tests to be able to start 'tests which pass this code' later @@ -966,6 +963,9 @@ dumb.aware.run.configurations.description=Enable executing run configurations in select.run.configuration.from.context=true select.run.configuration.from.context.description=Select run configuration when run from context (gutter icon, popup menu) +pause.indexing.in.power.save.mode=false +pause.indexing.in.power.save.mode.description=Whether indexing activities should be paused during Power Save Mode to reduce power consumption. If enabled, code insight functionality won't be available after changes to project model and after project opening. + ide.run.dashboard=false ide.run.dashboard.description=Enable Run Dashboard tool window ide.run.dashboard.restartRequired=true diff --git a/platform/util/src/com/intellij/Patches.java b/platform/util/src/com/intellij/Patches.java index f59bd79170d5..cfdc80f164e0 100644 --- a/platform/util/src/com/intellij/Patches.java +++ b/platform/util/src/com/intellij/Patches.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -104,12 +104,12 @@ public class Patches { public static final boolean JDK_MAC_FONT_STYLE_BUG = SystemInfo.isMac && !SystemInfo.isJavaVersionAtLeast("1.8.0_60"); /** - * On Mac OS font ligatures are not supported for natively loaded fonts, font needs to be loaded explicitly by JDK. + * On Mac OS font ligatures are not supported for natively loaded fonts, font needs to be loaded explicitly by JDK. */ public static final boolean JDK_BUG_ID_7162125; static { boolean value; - if (!SystemInfo.isMac || SystemInfo.isJavaVersionAtLeast("1.9")) value = false; + if (!SystemInfo.isMac || SystemInfo.isJavaVersionAtLeast("9")) value = false; else if (!SystemInfo.isJetbrainsJvm) value = true; else { try { diff --git a/platform/util/src/com/intellij/icons/AllIcons.java b/platform/util/src/com/intellij/icons/AllIcons.java index fd75c45118fa..044db7d05334 100644 --- a/platform/util/src/com/intellij/icons/AllIcons.java +++ b/platform/util/src/com/intellij/icons/AllIcons.java @@ -295,6 +295,7 @@ public class AllIcons { public static final Icon NewWatch = IconLoader.getIcon("/debugger/newWatch.png"); // 16x16 public static final Icon Question_badge = IconLoader.getIcon("/debugger/question_badge.png"); // 6x9 public static final Icon RestoreLayout = IconLoader.getIcon("/debugger/restoreLayout.png"); // 16x16 + public static final Icon Selfreference = IconLoader.getIcon("/debugger/selfreference.png"); // 16x16 public static final Icon ShowCurrentFrame = IconLoader.getIcon("/debugger/showCurrentFrame.png"); // 16x16 public static final Icon SmartStepInto = IconLoader.getIcon("/debugger/smartStepInto.png"); // 16x16 public static final Icon StackFrame = IconLoader.getIcon("/debugger/stackFrame.png"); // 16x16 diff --git a/platform/util/src/com/intellij/openapi/util/SystemInfo.java b/platform/util/src/com/intellij/openapi/util/SystemInfo.java index 3c4f065d0d37..1d1d0c0f5aa4 100644 --- a/platform/util/src/com/intellij/openapi/util/SystemInfo.java +++ b/platform/util/src/com/intellij/openapi/util/SystemInfo.java @@ -37,7 +37,6 @@ public class SystemInfo extends SystemInfoRt { public static final boolean isWindows = SystemInfoRt.isWindows; public static final boolean isMac = SystemInfoRt.isMac; - public static final boolean isOS2 = SystemInfoRt.isOS2; public static final boolean isLinux = SystemInfoRt.isLinux; public static final boolean isFreeBSD = SystemInfoRt.isFreeBSD; public static final boolean isSolaris = SystemInfoRt.isSolaris; @@ -238,5 +237,8 @@ public class SystemInfo extends SystemInfoRt { public static String getUnixReleaseVersion() { return null; } + + /** @deprecated outdated (to be removed in IDEA 2018) */ + public static final boolean isOS2 = SystemInfoRt.isOS2; // } \ No newline at end of file diff --git a/platform/util/src/com/intellij/util/ui/EmptyIcon.java b/platform/util/src/com/intellij/util/ui/EmptyIcon.java index df750196011c..98d85dbcf353 100644 --- a/platform/util/src/com/intellij/util/ui/EmptyIcon.java +++ b/platform/util/src/com/intellij/util/ui/EmptyIcon.java @@ -17,6 +17,7 @@ package com.intellij.util.ui; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.plaf.UIResource; @@ -115,7 +116,7 @@ public class EmptyIcon extends JBUI.CachingScalableJBIcon { @Override public EmptyIcon withJBUIPreScaled(boolean preScaled) { if (myUseCache && isJBUIPreScaled() != preScaled) { - Integer key = key(width, height, isJBUIPreScaled()); + Integer key = key(width, height); if (key != null) cache.remove(key); // rather useless to keep it in cache return create(width, height, preScaled); } @@ -123,7 +124,7 @@ public class EmptyIcon extends JBUI.CachingScalableJBIcon { } private static EmptyIcon create(int width, int height, boolean preScaled) { - Integer key = key(width, height, preScaled); + Integer key = key(width, height); EmptyIcon icon = key != null ? cache.get(key) : null; if (icon == null) { icon = new EmptyIcon(width, height, true); @@ -133,8 +134,9 @@ public class EmptyIcon extends JBUI.CachingScalableJBIcon { return icon; } - private static Integer key(int width, int height, boolean preScaled) { - return width == height && width < 129 ? preScaled ? width : JBUI.scale(width) : null; + @Nullable + private static Integer key(int width, int height) { + return width == height && width < 129 ? width : null; } @Override diff --git a/platform/util/src/com/intellij/util/ui/MacUIUtil.java b/platform/util/src/com/intellij/util/ui/MacUIUtil.java index 6b5d7782a612..3329d0531ba8 100644 --- a/platform/util/src/com/intellij/util/ui/MacUIUtil.java +++ b/platform/util/src/com/intellij/util/ui/MacUIUtil.java @@ -25,7 +25,6 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.border.Border; -import javax.swing.plaf.TreeUI; import javax.swing.tree.DefaultTreeCellRenderer; import java.awt.*; import java.awt.geom.GeneralPath; @@ -141,6 +140,7 @@ public class MacUIUtil { ((Graphics2D)g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f)); } + //noinspection UseJBColor g.setColor(new Color(100, 100, 100, 200)); g.drawRect(x1, y1, width1 - 1, height1 - 1); @@ -168,7 +168,7 @@ public class MacUIUtil { @Override public Insets getBorderInsets(Component c) { - return new Insets(6, 7, 6, 7); + return new JBInsets(6, 7, 6, 7); } @Override @@ -183,6 +183,7 @@ public class MacUIUtil { return (Color)o; } + //noinspection UseJBColor return new Color(64, 113, 167); } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsVFSListener.java b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsVFSListener.java index c3e930510121..5ea047adf1c1 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsVFSListener.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsVFSListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,8 @@ package com.intellij.openapi.vcs; import com.intellij.openapi.Disposable; -import com.intellij.openapi.command.CommandAdapter; import com.intellij.openapi.command.CommandEvent; +import com.intellij.openapi.command.CommandListener; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; @@ -86,7 +86,7 @@ public abstract class VcsVFSListener implements Disposable { myChangeListManager = ChangeListManager.getInstance(project); myDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject); - final MyVirtualFileAdapter myVFSListener = new MyVirtualFileAdapter(); + final MyVirtualFileListener myVFSListener = new MyVirtualFileListener(); final MyCommandAdapter myCommandListener = new MyCommandAdapter(); myVcsManager = ProjectLevelVcsManager.getInstance(project); @@ -327,7 +327,7 @@ public abstract class VcsVFSListener implements Disposable { protected abstract boolean isDirectoryVersioningSupported(); - private class MyVirtualFileAdapter extends VirtualFileAdapter { + private class MyVirtualFileListener implements VirtualFileListener { @Override public void fileCreated(@NotNull final VirtualFileEvent event) { VirtualFile file = event.getFile(); @@ -426,7 +426,7 @@ public abstract class VcsVFSListener implements Disposable { } } - private class MyCommandAdapter extends CommandAdapter { + private class MyCommandAdapter implements CommandListener { private int myCommandLevel; @Override diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/committed/VcsCommittedListsZipperAdapter.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/committed/VcsCommittedListsZipperAdapter.java index 24cdde7eb8e7..a3f45649ed47 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/committed/VcsCommittedListsZipperAdapter.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/committed/VcsCommittedListsZipperAdapter.java @@ -20,6 +20,7 @@ import com.intellij.openapi.vcs.RepositoryLocation; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; import com.intellij.util.containers.MultiMap; +import gnu.trove.THashSet; import java.util.ArrayList; import java.util.Collection; @@ -68,12 +69,13 @@ public abstract class VcsCommittedListsZipperAdapter implements VcsCommittedList return lists.get(0); } final CommittedChangeList result = lists.get(0); + + Set processed = new THashSet<>(result.getChanges()); + for (int i = 1; i < lists.size(); i++) { - final CommittedChangeList list = lists.get(i); - for (Change change : list.getChanges()) { - final Collection resultChanges = result.getChanges(); - if (! resultChanges.contains(change)) { - resultChanges.add(change); + for (Change change : lists.get(i).getChanges()) { + if (!processed.add(change)) { + result.getChanges().add(change); } } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/annotate/VFSForAnnotationListener.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/annotate/VFSForAnnotationListener.java index 97771282b6eb..e09bfa81418d 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/annotate/VFSForAnnotationListener.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/annotate/VFSForAnnotationListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,12 +17,12 @@ package com.intellij.openapi.vcs.annotate; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; +import com.intellij.openapi.vfs.VirtualFileListener; import com.intellij.openapi.vfs.VirtualFilePropertyEvent; import org.jetbrains.annotations.NotNull; -public class VFSForAnnotationListener extends VirtualFileAdapter { +public class VFSForAnnotationListener implements VirtualFileListener { private final VirtualFile myFile; private final FileAnnotation myFileAnnotation; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerImpl.java index 5cf16395d807..7332b6894349 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,6 @@ import com.intellij.openapi.vcs.impl.DefaultVcsRootPolicy; import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl; import com.intellij.openapi.vcs.impl.VcsInitObject; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.Function; import com.intellij.util.ReflectionUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; @@ -69,23 +68,21 @@ public class VcsDirtyScopeManagerImpl extends VcsDirtyScopeManager implements Pr @Override public void projectOpened() { - myVcsManager.addInitializationRequest(VcsInitObject.DIRTY_SCOPE_MANAGER, new Runnable() { - @Override - public void run() { - boolean ready = false; - synchronized (LOCK) { - if (!myProject.isDisposed() && myProject.isOpen()) { - myReady = ready = true; - } - } - if (ready) { - VcsDirtyScopeVfsListener.install(myProject); - markEverythingDirty(); + myVcsManager.addInitializationRequest(VcsInitObject.DIRTY_SCOPE_MANAGER, () -> { + boolean ready = false; + synchronized (LOCK) { + if (!myProject.isDisposed() && myProject.isOpen()) { + myReady = ready = true; } } + if (ready) { + VcsDirtyScopeVfsListener.install(myProject); + markEverythingDirty(); + } }); } + @Override public void markEverythingDirty() { if ((! myProject.isOpen()) || myProject.isDisposed() || myVcsManager.getAllActiveVcss().length == 0) return; @@ -102,10 +99,6 @@ public class VcsDirtyScopeManagerImpl extends VcsDirtyScopeManager implements Pr myChangeListManager.scheduleUpdate(); } - @Override - public void projectClosed() { - } - @Override @NotNull @NonNls public String getComponentName() { @@ -113,8 +106,6 @@ public class VcsDirtyScopeManagerImpl extends VcsDirtyScopeManager implements Pr } @Override - public void initComponent() {} - public void disposeComponent() { synchronized (LOCK) { myReady = false; @@ -178,6 +169,7 @@ public class VcsDirtyScopeManagerImpl extends VcsDirtyScopeManager implements Pr } } + @Override public void filesDirty(@Nullable final Collection filesDirty, @Nullable final Collection dirsRecursivelyDirty) { filePathsDirty(toFilePaths(filesDirty), toFilePaths(dirsRecursivelyDirty)); } @@ -185,12 +177,7 @@ public class VcsDirtyScopeManagerImpl extends VcsDirtyScopeManager implements Pr @NotNull private static Collection toFilePaths(@Nullable Collection files) { if (files == null) return Collections.emptyList(); - return ContainerUtil.map(files, new Function() { - @Override - public FilePath fun(VirtualFile virtualFile) { - return VcsUtil.getFilePath(virtualFile); - } - }); + return ContainerUtil.map(files, virtualFile -> VcsUtil.getFilePath(virtualFile)); } @Override @@ -298,17 +285,7 @@ public class VcsDirtyScopeManagerImpl extends VcsDirtyScopeManager implements Pr @NotNull private static String toString(@NotNull final MultiMap filesByVcs) { - return StringUtil.join(filesByVcs.keySet(), new Function() { - @Override - public String fun(@NotNull AbstractVcs vcs) { - return vcs.getName() + ": " + StringUtil.join(filesByVcs.get(vcs), new Function() { - @Override - public String fun(@NotNull FilePath path) { - return path.getPath(); - } - }, "\n"); - } - }, "\n"); + return StringUtil.join(filesByVcs.keySet(), vcs -> vcs.getName() + ": " + StringUtil.join(filesByVcs.get(vcs), path -> path.getPath(), "\n"), "\n"); } @Nullable diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java index 4386f8d23f9f..f7a8d30bd961 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -111,7 +111,7 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { private boolean myContainBasedChanges; private JLabel myPatchFileLabel; private PatchReader myReader; - private VirtualFileAdapter myListener; + private VirtualFileListener myListener; private final boolean myCanChangePatchFile; private String myHelpId = "reference.dialogs.vcs.patch.apply"; private final boolean myShouldUpdateChangeListName; @@ -160,6 +160,7 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { cleanNotifications(); myChangesTreeList = new MyChangeTreeList(project, Collections.emptyList(), new Runnable() { + @Override public void run() { final NamedLegendStatuses includedNameStatuses = new NamedLegendStatuses(); final Collection includedChanges = @@ -189,6 +190,7 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { myPatchFile = new TextFieldWithBrowseButton(); myPatchFile.addBrowseFolderListener(VcsBundle.message("patch.apply.select.title"), "", project, descriptor); myPatchFile.getTextField().getDocument().addDocumentListener(new DocumentAdapter() { + @Override protected void textChanged(DocumentEvent e) { setPathFileChangeDefault(); queueRequest(); @@ -243,7 +245,7 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { myPatchFile.setVisible(myCanChangePatchFile); if (myCanChangePatchFile) { - myListener = new VirtualFileAdapter() { + myListener = new VirtualFileListener() { @Override public void contentsChanged(@NotNull VirtualFileEvent event) { syncUpdatePatchFileAndScheduleReloadIfNeeded(event.getFile()); @@ -374,6 +376,7 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { } private class MyUpdater implements Runnable { + @Override public void run() { cleanNotifications(); final FilePresentationModel filePresentationModel = myRecentPathFileChange.get(); @@ -773,6 +776,7 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { myDirectorySelector = directorySelector; } + @Override public void run() { final FileChooserDescriptor descriptor = myDirectorySelector ? FileChooserDescriptorFactory.createSingleFolderDescriptor() @@ -907,14 +911,17 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { myIncluded = nameStatuses; } + @Override public int getNew() { return myTotal.getAdded(); } + @Override public int getModified() { return myTotal.getModified(); } + @Override public int getDeleted() { return myTotal.getDeleted(); } @@ -928,14 +935,17 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { return myTotal.getInapplicable(); } + @Override public int getIncludedNew() { return myIncluded.getAdded(); } + @Override public int getIncludedModified() { return myIncluded.getModified(); } + @Override public int getIncludedDeleted() { return myIncluded.getDeleted(); } @@ -947,6 +957,7 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { } private class MyChangeNodeDecorator implements ChangeNodeDecorator { + @Override public void decorate(Change change, SimpleColoredComponent component, boolean isShowFlatten) { if (change instanceof AbstractFilePatchInProgress.PatchChange) { final AbstractFilePatchInProgress.PatchChange patchChange = (AbstractFilePatchInProgress.PatchChange)change; @@ -984,6 +995,7 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { } } + @Override public void preDecorate(Change change, ChangesBrowserNodeRenderer renderer, boolean showFlatten) { } } @@ -1106,10 +1118,12 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { myMyChangeComparator = new MyChangeComparator(); } + @Override public void update(AnActionEvent e) { e.getPresentation().setEnabled((!myPatches.isEmpty()) && myContainBasedChanges); } + @Override public void actionPerformed(AnActionEvent e) { showDiff(); } @@ -1217,6 +1231,7 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { } private class MyChangeComparator implements Comparator { + @Override public int compare(AbstractFilePatchInProgress.PatchChange o1, AbstractFilePatchInProgress.PatchChange o2) { if (PropertiesComponent.getInstance(myProject).isTrueValue("ChangesBrowser.SHOW_FLATTEN")) { return o1.getPatchInProgress().getIoCurrentBase().getName().compareTo(o2.getPatchInProgress().getIoCurrentBase().getName()); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java index 2e3d31425e0b..26c2799b2344 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -121,6 +121,7 @@ public class ShelvedChangesViewManager implements ProjectComponent { myContentManager = contentManager; myShelveChangesManager = shelveChangesManager; bus.connect().subscribe(ShelveChangesManager.SHELF_TOPIC, new ChangeListener() { + @Override public void stateChanged(ChangeEvent e) { myUpdatePending = true; ApplicationManager.getApplication().invokeLater(() -> updateChangesContent(), ModalityState.NON_MODAL); @@ -154,6 +155,7 @@ public class ShelvedChangesViewManager implements ProjectComponent { }.installOn(myTree); new TreeSpeedSearch(myTree, new Convertor() { + @Override public String convert(TreePath o) { final Object lc = o.getLastPathComponent(); final Object lastComponent = lc == null ? null : ((DefaultMutableTreeNode) lc).getUserObject(); @@ -174,6 +176,7 @@ public class ShelvedChangesViewManager implements ProjectComponent { }, true); } + @Override public void projectOpened() { StartupManager startupManager = StartupManager.getInstance(myProject); if (startupManager == null) { @@ -183,20 +186,12 @@ public class ShelvedChangesViewManager implements ProjectComponent { startupManager.registerPostStartupActivity((DumbAwareRunnable)() -> updateChangesContent()); } - public void projectClosed() { - } - + @Override @NonNls @NotNull public String getComponentName() { return "ShelvedChangesViewManager"; } - public void initComponent() { - } - - public void disposeComponent() { - } - private void updateChangesContent() { myUpdatePending = false; final List changeLists = new ArrayList<>(myShelveChangesManager.getShelvedChangeLists()); @@ -457,6 +452,7 @@ public class ShelvedChangesViewManager implements ProjectComponent { return ourInstance; } + @Override public int compare(final Object o1, final Object o2) { final String path1 = getPath(o1); final String path2 = getPath(o2); @@ -503,6 +499,7 @@ public class ShelvedChangesViewManager implements ProjectComponent { myIssueLinkRenderer = new IssueLinkRenderer(project, this); } + @Override public void customizeCellRenderer(@NotNull JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) value; Object nodeValue = node.getUserObject(); @@ -563,6 +560,7 @@ public class ShelvedChangesViewManager implements ProjectComponent { private class MyShelveDeleteProvider implements DeleteProvider { + @Override public void deleteElement(@NotNull DataContext dataContext) { final Project project = CommonDataKeys.PROJECT.getData(dataContext); if (project == null) return; @@ -662,6 +660,7 @@ public class ShelvedChangesViewManager implements ProjectComponent { } } + @Override public boolean canDeleteElement(@NotNull DataContext dataContext) { return !getShelvedLists(dataContext).isEmpty(); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/LineStatusTrackerManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/LineStatusTrackerManager.java index fde3d370f96d..5fb83694c269 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/LineStatusTrackerManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/LineStatusTrackerManager.java @@ -426,7 +426,7 @@ public class LineStatusTrackerManager implements ProjectComponent, LineStatusTra } } - private class MyVirtualFileListener extends VirtualFileAdapter { + private class MyVirtualFileListener implements VirtualFileListener { @Override public void beforeContentsChange(@NotNull VirtualFileEvent event) { if (event.isFromRefresh()) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/update/RestoreUpdateTree.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/update/RestoreUpdateTree.java index 67918d0b0e11..478656a3237e 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/update/RestoreUpdateTree.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/update/RestoreUpdateTree.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -66,23 +66,12 @@ public class RestoreUpdateTree implements ProjectComponent, PersistentStateCompo }); } - @Override - public void projectClosed() { - } - @Override @NotNull public String getComponentName() { return "RestoreUpdateTree"; } - @Override - public void initComponent() { } - - @Override - public void disposeComponent() { - } - @Nullable @Override public Element getState() { diff --git a/platform/vcs-impl/src/com/intellij/platform/PlatformVcsDetector.java b/platform/vcs-impl/src/com/intellij/platform/PlatformVcsDetector.java index 4bd8758bb7e9..16f152ed143c 100644 --- a/platform/vcs-impl/src/com/intellij/platform/PlatformVcsDetector.java +++ b/platform/vcs-impl/src/com/intellij/platform/PlatformVcsDetector.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,13 +20,10 @@ import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.project.DumbAwareRunnable; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupManager; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.openapi.vcs.VcsDirectoryMapping; import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl; import com.intellij.openapi.vfs.VirtualFile; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; /** * @author yole @@ -40,8 +37,10 @@ public class PlatformVcsDetector implements ProjectComponent { myVcsManager = vcsManager; } + @Override public void projectOpened() { StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new DumbAwareRunnable() { + @Override public void run() { final DumbAwareRunnable runnable = new DumbAwareRunnable() { @Override @@ -58,28 +57,8 @@ public class PlatformVcsDetector implements ProjectComponent { } } }; - ApplicationManager.getApplication().invokeLater(runnable, new Condition() { - @Override - public boolean value(Object o) { - return (! myProject.isOpen()) || myProject.isDisposed(); - } - }); + ApplicationManager.getApplication().invokeLater(runnable, o -> (! myProject.isOpen()) || myProject.isDisposed()); } }); } - - public void projectClosed() { - } - - @NonNls - @NotNull - public String getComponentName() { - return "PlatformVcsDetector"; - } - - public void initComponent() { - } - - public void disposeComponent() { - } } diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/XLineBreakpointType.java b/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/XLineBreakpointType.java index d526ead2df76..47320ec0354d 100644 --- a/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/XLineBreakpointType.java +++ b/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/XLineBreakpointType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -76,10 +76,10 @@ public abstract class XLineBreakpointType

exten } /** - * Source position for line breakpoint is determined by its file and line + * Source position for line breakpoint by default is determined by its file and line */ @Override - public final XSourcePosition getSourcePosition(@NotNull XBreakpoint

breakpoint) { + public XSourcePosition getSourcePosition(@NotNull XBreakpoint

breakpoint) { return null; } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointImpl.java index 47d67064a56b..9ca82c47ad7a 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ package com.intellij.xdebugger.impl.breakpoints; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ReadAction; -import com.intellij.openapi.application.Result; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.EditorColorsScheme; @@ -148,7 +146,7 @@ public class XLineBreakpointImpl

extends XBreak } @Nullable - private VirtualFile getFile() { + public VirtualFile getFile() { return VirtualFileManager.getInstance().findFileByUrl(getFileUrl()); } @@ -191,13 +189,12 @@ public class XLineBreakpointImpl

extends XBreak @Override public XSourcePosition getSourcePosition() { + if (mySourcePosition != null) { + return mySourcePosition; + } + mySourcePosition = super.getSourcePosition(); if (mySourcePosition == null) { - new ReadAction() { - @Override - protected void run(@NotNull Result result) { - mySourcePosition = XDebuggerUtil.getInstance().createPosition(getFile(), getLine()); - } - }.execute(); + mySourcePosition = XDebuggerUtil.getInstance().createPosition(getFile(), getLine()); } return mySourcePosition; } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/EquivalenceChecker.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/EquivalenceChecker.java index 56026648d787..3da752ad75a4 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/EquivalenceChecker.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/EquivalenceChecker.java @@ -749,7 +749,7 @@ public class EquivalenceChecker { final PsiExpression[] args2 = argumentList2.getExpressions(); match = match.combine(expressionsAreEquivalent(args1, args2)); - if (match.isPartialMatch()) { + if (args1.length != 0 && match.isPartialMatch()) { final PsiElement leftDiff = match.getLeftDiff(); final PsiExpression lastArg = args1[args1.length - 1]; if (Comparing.equal(leftDiff, lastArg)) { diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryFinalOnLocalVariableOrParameterInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryFinalOnLocalVariableOrParameterInspection.java index e279837de999..51652fbb4bf8 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryFinalOnLocalVariableOrParameterInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryFinalOnLocalVariableOrParameterInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2013 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2017 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -149,14 +149,13 @@ public class UnnecessaryFinalOnLocalVariableOrParameterInspection extends BaseIn if (declaredElements.length == 0) { return; } - for (final PsiElement declaredElement : declaredElements) { - if (!(declaredElement instanceof PsiLocalVariable)) { - return; - } - final PsiLocalVariable variable = (PsiLocalVariable)declaredElement; - if (!variable.hasModifierProperty(PsiModifier.FINAL)) { - return; - } + final PsiElement firstElement = declaredElements[0]; + if (!(firstElement instanceof PsiLocalVariable)) { + return; + } + final PsiLocalVariable firstVariable = (PsiLocalVariable)firstElement; + if (!firstVariable.hasModifierProperty(PsiModifier.FINAL)) { + return; } final PsiCodeBlock containingBlock = PsiTreeUtil.getParentOfType(statement, PsiCodeBlock.class); if (containingBlock == null) { @@ -164,11 +163,11 @@ public class UnnecessaryFinalOnLocalVariableOrParameterInspection extends BaseIn } for (PsiElement declaredElement : declaredElements) { final PsiLocalVariable variable = (PsiLocalVariable)declaredElement; - if (isNecessaryFinal(containingBlock, variable)) { + if (isNecessaryFinal(variable, containingBlock)) { return; } } - final PsiLocalVariable variable = (PsiLocalVariable)statement.getDeclaredElements()[0]; + final PsiLocalVariable variable = (PsiLocalVariable)firstElement; registerModifierError(PsiModifier.FINAL, variable, variable); } @@ -189,21 +188,12 @@ public class UnnecessaryFinalOnLocalVariableOrParameterInspection extends BaseIn if (!parameter.hasModifierProperty(PsiModifier.FINAL)) { return; } - final PsiClass containingClass = method.getContainingClass(); - if (containingClass != null) { - if (containingClass.isInterface() || containingClass.isAnnotationType()) { - registerModifierError(PsiModifier.FINAL, parameter, parameter); - return; - } - } if (method.hasModifierProperty(PsiModifier.ABSTRACT)) { registerModifierError(PsiModifier.FINAL, parameter, parameter); - return; } - if (onlyWarnOnAbstractMethods) { - return; + else if (!onlyWarnOnAbstractMethods) { + check(parameter); } - registerError(method, parameter); } @Override @@ -222,7 +212,7 @@ public class UnnecessaryFinalOnLocalVariableOrParameterInspection extends BaseIn if (!parameter.hasModifierProperty(PsiModifier.FINAL)) { continue; } - registerError(catchBlock, parameter); + check(parameter); } } @@ -236,15 +226,15 @@ public class UnnecessaryFinalOnLocalVariableOrParameterInspection extends BaseIn if (!parameter.hasModifierProperty(PsiModifier.FINAL)) { return; } - registerError(statement, parameter); + check(parameter); } - private boolean isNecessaryFinal(PsiElement method, PsiVariable parameter) { - return !PsiUtil.isLanguageLevel8OrHigher(parameter) && VariableAccessUtils.variableIsUsedInInnerClass(parameter, method); + private boolean isNecessaryFinal(PsiVariable parameter, PsiElement context) { + return !PsiUtil.isLanguageLevel8OrHigher(parameter) && VariableAccessUtils.variableIsUsedInInnerClass(parameter, context); } - private void registerError(PsiElement context, PsiVariable parameter) { - if (!isNecessaryFinal(context, parameter)) { + private void check(PsiParameter parameter) { + if (!isNecessaryFinal(parameter, parameter.getDeclarationScope())) { registerModifierError(PsiModifier.FINAL, parameter, parameter); } } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unnecessary_final_on_local_variable_or_parameter/UnnecessaryFinalOnLocalVariableOrParameter.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unnecessary_final_on_local_variable_or_parameter/UnnecessaryFinalOnLocalVariableOrParameter.java index cac79ba6a886..49c9e121f076 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unnecessary_final_on_local_variable_or_parameter/UnnecessaryFinalOnLocalVariableOrParameter.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unnecessary_final_on_local_variable_or_parameter/UnnecessaryFinalOnLocalVariableOrParameter.java @@ -4,7 +4,7 @@ public class UnnecessaryFinalOnLocalVariableOrParameter { class XX { XX(Object o) {} - void foo(final Object o) { + void foo(final Object o) { new XX(o) {}; } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unnecessary_final_on_local_variable_or_parameter/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unnecessary_final_on_local_variable_or_parameter/expected.xml deleted file mode 100644 index f24143dd4792..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unnecessary_final_on_local_variable_or_parameter/expected.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - UnnecessaryFinalOnLocalVariableOrParameter.java - 7 - Unnecessary 'final' on local variable or parameter - Unnecessary <code>final</code> on parameter 'o' #loc - - \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnnecessaryFinalOnLocalVariableOrParameterInspectionLightTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnnecessaryFinalOnLocalVariableOrParameterInspectionJava8Test.java similarity index 74% rename from plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnnecessaryFinalOnLocalVariableOrParameterInspectionLightTest.java rename to plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnnecessaryFinalOnLocalVariableOrParameterInspectionJava8Test.java index 4486c99065ff..1fee3f2488f8 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnnecessaryFinalOnLocalVariableOrParameterInspectionLightTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnnecessaryFinalOnLocalVariableOrParameterInspectionJava8Test.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ import com.intellij.codeInspection.InspectionProfileEntry; import com.siyeh.ig.LightInspectionTestCase; import org.jetbrains.annotations.Nullable; -public class UnnecessaryFinalOnLocalVariableOrParameterInspectionLightTest extends LightInspectionTestCase { +public class UnnecessaryFinalOnLocalVariableOrParameterInspectionJava8Test extends LightInspectionTestCase { public void testFinalWithoutInnerClass() throws Exception { doTest("class Issue {\n" + @@ -41,6 +41,17 @@ public class UnnecessaryFinalOnLocalVariableOrParameterInspectionLightTest exten "}"); } + public void testInterfaceMethods() { + final UnnecessaryFinalOnLocalVariableOrParameterInspection inspection = new UnnecessaryFinalOnLocalVariableOrParameterInspection(); + inspection.onlyWarnOnAbstractMethods = true; + myFixture.enableInspections(inspection); + doTest("interface X {" + + " default void m(final String s) {}" + + " static void n(final String s) {}" + + " void o(/*Unnecessary 'final' on parameter 's'*/final/**/ String s);" + + "}"); + } + @Nullable @Override protected InspectionProfileEntry getInspection() { diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnnecessaryFinalOnLocalVariableOrParameterInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnnecessaryFinalOnLocalVariableOrParameterInspectionTest.java index 2dcbf3dfe287..41197d16390a 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnnecessaryFinalOnLocalVariableOrParameterInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnnecessaryFinalOnLocalVariableOrParameterInspectionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,19 +15,27 @@ */ package com.siyeh.ig.style; -import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.testFramework.IdeaTestUtil; -import com.siyeh.ig.IGInspectionTestCase; +import com.intellij.codeInspection.InspectionProfileEntry; +import com.intellij.testFramework.LightProjectDescriptor; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -public class UnnecessaryFinalOnLocalVariableOrParameterInspectionTest extends IGInspectionTestCase { +public class UnnecessaryFinalOnLocalVariableOrParameterInspectionTest extends LightInspectionTestCase { + + @NotNull @Override - protected Sdk getTestProjectSdk() { - // effectively final rules are different in jdk 8 - return IdeaTestUtil.getMockJdk17(); + protected LightProjectDescriptor getProjectDescriptor() { + return JAVA_1_7; } - public void test() throws Exception { - doTest("com/siyeh/igtest/style/unnecessary_final_on_local_variable_or_parameter", - new UnnecessaryFinalOnLocalVariableOrParameterInspection()); + public void testUnnecessaryFinalOnLocalVariableOrParameter() throws Exception { + doTest(); + } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new UnnecessaryFinalOnLocalVariableOrParameterInspection(); } } diff --git a/plugins/ant/src/com/intellij/lang/ant/config/impl/AntConfigurationImpl.java b/plugins/ant/src/com/intellij/lang/ant/config/impl/AntConfigurationImpl.java index 6f6d9e8ef8ff..564ba38e2d73 100644 --- a/plugins/ant/src/com/intellij/lang/ant/config/impl/AntConfigurationImpl.java +++ b/plugins/ant/src/com/intellij/lang/ant/config/impl/AntConfigurationImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,8 +45,8 @@ import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; +import com.intellij.openapi.vfs.VirtualFileListener; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; @@ -73,15 +73,18 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis public static final ValueProperty DEFAULT_ANT = new ValueProperty<>("defaultAnt", AntReference.BUNDLED_ANT); private static final ValueProperty INSTANCE = new ValueProperty<>("$instance", null); public static final AbstractProperty DEFAULT_JDK_NAME = new AbstractProperty() { + @Override public String getName() { return "$defaultJDKName"; } + @Override @Nullable public String getDefault(final AbstractPropertyContainer container) { return get(container); } + @Override @Nullable public String get(@NotNull AbstractPropertyContainer container) { if (!container.hasProperty(this)) { @@ -92,6 +95,7 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis return ProjectRootManager.getInstance(antConfiguration.getProject()).getProjectSdkName(); } + @Override public String copy(final String jdkName) { return jdkName; } @@ -128,15 +132,19 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis myPsiManager = PsiManager.getInstance(project); myStartupManager = StartupManager.getInstance(project); addAntConfigurationListener(new AntConfigurationListener() { + @Override public void configurationLoaded() { restartDaemon(); } + @Override public void buildFileChanged(final AntBuildFile buildFile) { restartDaemon(); } + @Override public void buildFileAdded(final AntBuildFile buildFile) { restartDaemon(); } + @Override public void buildFileRemoved(final AntBuildFile buildFile) { restartDaemon(); } @@ -150,7 +158,8 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis } } }); - VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() { + VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileListener() { + @Override public void beforeFileDeletion(@NotNull final VirtualFileEvent event) { final VirtualFile vFile = event.getFile(); // cleanup @@ -171,6 +180,7 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis } + @Override public Element getState() { try { final Element state = new Element("state"); @@ -187,7 +197,7 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis final List files = new ArrayList<>(myAntFileToContextFileMap.keySet()); // sort in order to minimize changes - Collections.sort(files, (o1, o2) -> o1.getUrl().compareTo(o2.getUrl())); + Collections.sort(files, Comparator.comparing(VirtualFile::getUrl)); for (VirtualFile file : files) { final Element element = new Element(CONTEXT_MAPPING); final VirtualFile contextFile = myAntFileToContextFileMap.get(file); @@ -204,6 +214,7 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis } } + @Override public void loadState(Element state) { myIsInitialized = Boolean.FALSE; try { @@ -241,6 +252,7 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis runWhenInitialized(() -> { String title = AntBundle.message("loading.ant.config.progress"); queueLater(new Task.Backgroundable(getProject(), title, false) { + @Override public void run(@NotNull final ProgressIndicator indicator) { if (getProject().isDisposed()) { return; @@ -357,11 +369,13 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis private volatile Boolean myIsInitialized; private volatile Thread myInitThread; + @Override public boolean isInitialized() { final Boolean initialized = myIsInitialized; return initialized == null || initialized.booleanValue(); } + @Override public AntBuildFile[] getBuildFiles() { //noinspection SuspiciousToArrayCall return myBuildFiles.toArray(new AntBuildFileBase[myBuildFiles.size()]); @@ -372,16 +386,19 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis return myBuildFiles; } + @Override public AntBuildFile addBuildFile(final VirtualFile file) throws AntNoFileException { final AntBuildFile[] result = new AntBuildFile[]{null}; final AntNoFileException[] ex = new AntNoFileException[]{null}; final String title = AntBundle.message("register.ant.build.progress", file.getPresentableUrl()); ProgressManager.getInstance().run(new Task.Modal(getProject(), title, false) { - @Nullable + @NotNull + @Override public NotificationInfo getNotificationInfo() { return new NotificationInfo("Ant", "Ant Task Finished", ""); } + @Override public void run(@NotNull final ProgressIndicator indicator) { indicator.setIndeterminate(true); indicator.pushState(); @@ -412,6 +429,7 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis return result[0]; } + @Override public void removeBuildFile(@NotNull AntBuildFile file) { incModificationCount(); removeBuildFileImpl(file); @@ -419,22 +437,27 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis updateRegisteredActions(); } + @Override public void addAntConfigurationListener(final AntConfigurationListener listener) { myEventDispatcher.addListener(listener); } + @Override public void removeAntConfigurationListener(final AntConfigurationListener listener) { myEventDispatcher.removeListener(listener); } + @Override public boolean isFilterTargets() { return myAntWorkspaceConfiguration.FILTER_TARGETS; } + @Override public void setFilterTargets(final boolean value) { myAntWorkspaceConfiguration.FILTER_TARGETS = value; } + @Override public AntBuildTarget[] getMetaTargets(final AntBuildFile buildFile) { final List events = getEventsByClass(ExecuteCompositeTargetEvent.class); if (events.size() == 0) { @@ -444,6 +467,7 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis .filter(target -> target != null && buildFile.equals(target.getBuildFile())).toArray(AntBuildTarget[]::new); } + @Override public List getEventsForTarget(final AntBuildTarget target) { final List list = new ArrayList<>(); synchronized (myEventToTargetMap) { @@ -457,6 +481,7 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis return list; } + @Override @Nullable public AntBuildTarget getTargetForEvent(final ExecutionEvent event) { final Pair pair; @@ -490,36 +515,43 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis return null; } + @Override public void setTargetForEvent(final AntBuildFile buildFile, final String targetName, final ExecutionEvent event) { synchronized (myEventToTargetMap) { myEventToTargetMap.put(event, Pair.create(buildFile, targetName)); } } + @Override public void clearTargetForEvent(final ExecutionEvent event) { synchronized (myEventToTargetMap) { myEventToTargetMap.remove(event); } } + @Override public void updateBuildFile(final AntBuildFile buildFile) { incModificationCount(); myEventDispatcher.getMulticaster().buildFileChanged(buildFile); updateRegisteredActions(); } + @Override public boolean isAutoScrollToSource() { return myAntWorkspaceConfiguration.IS_AUTOSCROLL_TO_SOURCE; } + @Override public void setAutoScrollToSource(final boolean value) { myAntWorkspaceConfiguration.IS_AUTOSCROLL_TO_SOURCE = value; } + @Override public AntInstallation getProjectDefaultAnt() { return DEFAULT_ANT.get(getProperties()).find(GlobalAntConfiguration.getInstance()); } + @Override @Nullable public AntBuildModelBase getModelIfRegistered(@NotNull AntBuildFileBase buildFile) { return myBuildFiles.contains(buildFile) ? getModel(buildFile) : null; @@ -566,6 +598,7 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis } } + @Override public AntBuildModelBase getModel(@NotNull AntBuildFile buildFile) { AntBuildModelBase model = myModelToBuildFileMap.get(buildFile); if (model == null) { @@ -575,6 +608,7 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis return model; } + @Override @Nullable public AntBuildFile findBuildFileByActionId(final String id) { for (AntBuildFile buildFile : myBuildFiles) { @@ -674,10 +708,12 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis myEventDispatcher.getMulticaster().buildFileRemoved(buildFile); } + @Override public boolean executeTargetBeforeCompile(final DataContext context) { return runTargetSynchronously(context, ExecuteBeforeCompilationEvent.getInstance()); } + @Override public boolean executeTargetAfterCompile(final DataContext context) { return runTargetSynchronously(context, ExecuteAfterCompilationEvent.getInstance()); } @@ -711,6 +747,7 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis } else { target.run(dataContext, additionalProperties, new AntBuildListener() { + @Override public void buildFinished(int state, int errorCount) { result.set((state == AntBuildListener.FINISHED_SUCCESSFULLY) && (errorCount == 0)); targetDone.up(); @@ -791,6 +828,7 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis } } + @Override public void setContextFile(@NotNull XmlFile file, @Nullable XmlFile context) { if (context != null) { myAntFileToContextFileMap.put(file.getVirtualFile(), context.getVirtualFile()); @@ -800,6 +838,7 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis } } + @Override @Nullable public XmlFile getContextFile(@Nullable final XmlFile file) { if (file == null) { @@ -817,6 +856,7 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis return AntDomFileDescription.isAntFile(xmlFile)? xmlFile : null; } + @Override @Nullable public AntBuildFileBase getAntBuildFile(@NotNull PsiFile file) { final VirtualFile vFile = file.getVirtualFile(); @@ -830,6 +870,7 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis return null; } + @Override @Nullable public XmlFile getEffectiveContextFile(final XmlFile file) { return new Object() { @@ -853,6 +894,7 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis ExecuteCompositeTargetEvent.PRESENTABLE_NAME }; + @Override public int compare(final Element o1, final Element o2) { for (String attribName : COMPARABLE_ATTRIB_NAMES) { final int valuesEqual = Comparing.compare(o1.getAttributeValue(attribName), o2.getAttributeValue(attribName)); diff --git a/plugins/copyright/src/com/maddyhome/idea/copyright/util/NewFileTracker.java b/plugins/copyright/src/com/maddyhome/idea/copyright/util/NewFileTracker.java index 84a91150c35a..e24b53bf4061 100644 --- a/plugins/copyright/src/com/maddyhome/idea/copyright/util/NewFileTracker.java +++ b/plugins/copyright/src/com/maddyhome/idea/copyright/util/NewFileTracker.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ public class NewFileTracker { } public NewFileTracker() { - VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() { + VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileListener() { @Override public void fileCreated(@NotNull VirtualFileEvent event) { if (event.isFromRefresh()) return; diff --git a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/application/CvsEntriesManager.java b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/application/CvsEntriesManager.java index 67656648e7d9..10ec8199b7ff 100644 --- a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/application/CvsEntriesManager.java +++ b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/application/CvsEntriesManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,7 +49,7 @@ import java.util.Map; * author: lesya */ -public class CvsEntriesManager extends VirtualFileAdapter { +public class CvsEntriesManager implements VirtualFileListener { private static final Logger LOG = Logger.getInstance("#com.intellij.cvsSupport2.application.CvsEntriesManager"); diff --git a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/application/CvsStorageComponent.java b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/application/CvsStorageComponent.java index d3af0730f23a..0eff36578d93 100644 --- a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/application/CvsStorageComponent.java +++ b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/application/CvsStorageComponent.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,9 +17,9 @@ package com.intellij.cvsSupport2.application; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileAdapter; +import com.intellij.openapi.vfs.VirtualFileListener; -public abstract class CvsStorageComponent extends VirtualFileAdapter { +public abstract class CvsStorageComponent implements VirtualFileListener { protected boolean myIsActive = false; public static final CvsStorageComponent ABSENT_STORAGE = new CvsStorageComponent() { diff --git a/plugins/devkit/devkit.iml b/plugins/devkit/devkit.iml index 195eb207cf03..a08d984f3612 100644 --- a/plugins/devkit/devkit.iml +++ b/plugins/devkit/devkit.iml @@ -39,5 +39,6 @@ + \ No newline at end of file diff --git a/plugins/devkit/resources/META-INF/plugin.xml b/plugins/devkit/resources/META-INF/plugin.xml index 36ce0e963114..0800326f69b3 100644 --- a/plugins/devkit/resources/META-INF/plugin.xml +++ b/plugins/devkit/resources/META-INF/plugin.xml @@ -125,7 +125,7 @@ - diff --git a/plugins/devkit/src/inspections/DevKitUastInspectionBase.java b/plugins/devkit/src/inspections/DevKitUastInspectionBase.java new file mode 100644 index 000000000000..ff5be3f07b3f --- /dev/null +++ b/plugins/devkit/src/inspections/DevKitUastInspectionBase.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.devkit.inspections; + +import com.intellij.codeInspection.AbstractBaseUastLocalInspectionTool; +import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtilCore; +import com.intellij.psi.PsiElementVisitor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.devkit.module.PluginModuleType; +import org.jetbrains.idea.devkit.util.PsiUtil; + +/** + * @author yole + */ +public abstract class DevKitUastInspectionBase extends AbstractBaseUastLocalInspectionTool { + + @NotNull + @Override + public final PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + return isAllowed(holder) ? buildInternalVisitor(holder, isOnTheFly) : PsiElementVisitor.EMPTY_VISITOR; + } + + protected boolean isAllowed(ProblemsHolder holder) { + if (PsiUtil.isIdeaProject(holder.getProject())) { + return true; + } + + Module module = ModuleUtilCore.findModuleForPsiElement(holder.getFile()); + if (module == null) { + return false; + } + + if (PluginModuleType.isPluginModuleOrDependency(module)) { + return true; + } + + if (PsiUtil.isPluginModule(module)) { + return true; + } + + // always run in tests + return ApplicationManager.getApplication().isUnitTestMode(); + } + + protected PsiElementVisitor buildInternalVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + return super.buildVisitor(holder, isOnTheFly); + } +} diff --git a/plugins/devkit/src/inspections/StatefulEpInspection.java b/plugins/devkit/src/inspections/StatefulEpInspection.java index e2747dbd169e..3af76add1480 100644 --- a/plugins/devkit/src/inspections/StatefulEpInspection.java +++ b/plugins/devkit/src/inspections/StatefulEpInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,19 +21,23 @@ import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.project.Project; -import com.intellij.psi.*; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiField; +import com.intellij.psi.PsiModifier; +import com.intellij.psi.PsiReference; import com.intellij.psi.util.InheritanceUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.devkit.util.ExtensionPointLocator; +import org.jetbrains.uast.UClass; import java.util.List; -public class StatefulEpInspection extends DevKitInspectionBase { +public class StatefulEpInspection extends DevKitUastInspectionBase { @Nullable @Override - public ProblemDescriptor[] checkClass(@NotNull PsiClass psiClass, @NotNull InspectionManager manager, boolean isOnTheFly) { + public ProblemDescriptor[] checkClass(@NotNull UClass psiClass, @NotNull InspectionManager manager, boolean isOnTheFly) { PsiField[] fields = psiClass.getFields(); if (fields.length == 0) return super.checkClass(psiClass, manager, isOnTheFly); diff --git a/plugins/devkit/src/testAssistant/NavigateToTestDataAction.java b/plugins/devkit/src/testAssistant/NavigateToTestDataAction.java index 388cb90d0ab9..3560055ecff4 100644 --- a/plugins/devkit/src/testAssistant/NavigateToTestDataAction.java +++ b/plugins/devkit/src/testAssistant/NavigateToTestDataAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,7 @@ import com.intellij.testFramework.Parameterized; import com.intellij.ui.awt.RelativePoint; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.uast.*; import java.util.Collections; import java.util.List; @@ -86,15 +87,17 @@ public class NavigateToTestDataAction extends AnAction implements TestTreeViewAc if (location instanceof PsiMemberParameterizedLocation) { PsiClass containingClass = ((PsiMemberParameterizedLocation)location).getContainingClass(); if (containingClass == null) { - containingClass = PsiTreeUtil.getParentOfType(location.getPsiElement(), PsiClass.class, false); + containingClass = UastContextKt.getUastParentOfType(location.getPsiElement(), UClass.class, false); } if (containingClass != null) { - final PsiAnnotation annotation = AnnotationUtil.findAnnotationInHierarchy(containingClass, Collections.singleton(JUnitUtil.RUN_WITH)); + final UAnnotation annotation = + UastContextKt.toUElement(AnnotationUtil.findAnnotationInHierarchy(containingClass, Collections.singleton(JUnitUtil.RUN_WITH)), UAnnotation.class); if (annotation != null) { - final PsiAnnotationMemberValue memberValue = annotation.findAttributeValue("value"); - if (memberValue instanceof PsiClassObjectAccessExpression) { - final PsiTypeElement operand = ((PsiClassObjectAccessExpression)memberValue).getOperand(); - if (operand.getType().equalsToText(Parameterized.class.getName())) { + UExpression value = annotation.findAttributeValue("value"); + if (value instanceof UClassLiteralExpression) { + UClassLiteralExpression classLiteralExpression = (UClassLiteralExpression)value; + PsiType type = classLiteralExpression.getType(); + if (type != null && type.equalsToText(Parameterized.class.getName())) { final String testDataPath = TestDataLineMarkerProvider.getTestDataBasePath(containingClass); final String paramSetName = ((PsiMemberParameterizedLocation)location).getParamSetName(); final String baseFileName = StringUtil.trimEnd(StringUtil.trimStart(paramSetName, "["), "]"); @@ -126,8 +129,7 @@ public class NavigateToTestDataAction extends AnAction implements TestTreeViewAc final Editor editor = CommonDataKeys.EDITOR.getData(context); final PsiFile file = CommonDataKeys.PSI_FILE.getData(context); if (file != null && editor != null) { - PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); - return PsiTreeUtil.getParentOfType(element, PsiMethod.class); + return UastContextKt.findUElementAt(file, editor.getCaretModel().getOffset(), UMethod.class); } return null; diff --git a/plugins/devkit/src/testAssistant/TestDataAsRelatedFileProvider.java b/plugins/devkit/src/testAssistant/TestDataAsRelatedFileProvider.java index 748171721e13..94a576ccb6bc 100644 --- a/plugins/devkit/src/testAssistant/TestDataAsRelatedFileProvider.java +++ b/plugins/devkit/src/testAssistant/TestDataAsRelatedFileProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,8 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethod; import org.jetbrains.annotations.NotNull; +import org.jetbrains.uast.UMethod; +import org.jetbrains.uast.UastContextKt; import java.util.Collections; import java.util.List; @@ -41,20 +43,14 @@ public class TestDataAsRelatedFileProvider extends GotoRelatedProvider { final Project project = context.getData(CommonDataKeys.PROJECT); final PsiElement element = context.getData(CommonDataKeys.PSI_ELEMENT); if (editor == null || element == null || project == null) { - return Collections.emptyList(); + return Collections.emptyList(); } - PsiMethod method = null; - for (PsiElement e = element; e != null; e = e.getParent()) { - if (e instanceof PsiMethod) { - method = (PsiMethod)e; - break; - } - } + PsiMethod method = UastContextKt.getUastParentOfType(element, UMethod.class); if (method == null) { return Collections.emptyList(); - } - + } + final List testDataFiles = NavigateToTestDataAction.findTestDataFiles(context); if (testDataFiles == null || testDataFiles.isEmpty()) { return Collections.emptyList(); diff --git a/plugins/devkit/src/testAssistant/TestDataLineMarkerProvider.java b/plugins/devkit/src/testAssistant/TestDataLineMarkerProvider.java index f6697274ef71..ec1140261f1a 100644 --- a/plugins/devkit/src/testAssistant/TestDataLineMarkerProvider.java +++ b/plugins/devkit/src/testAssistant/TestDataLineMarkerProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,9 @@ import com.intellij.psi.util.PsiUtilCore; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.devkit.util.PsiUtil; +import org.jetbrains.uast.UAnnotation; +import org.jetbrains.uast.UExpression; +import org.jetbrains.uast.UastContextKt; import java.util.Collections; @@ -73,14 +76,13 @@ public class TestDataLineMarkerProvider extends RunLineMarkerContributor { public static String getTestDataBasePath(@Nullable PsiClass psiClass) { if (psiClass == null) return null; - final PsiAnnotation annotation = - AnnotationUtil.findAnnotationInHierarchy(psiClass, Collections.singleton(TEST_DATA_PATH_ANNOTATION_QUALIFIED_NAME)); + final UAnnotation annotation = + UastContextKt.toUElement(AnnotationUtil.findAnnotationInHierarchy(psiClass, Collections.singleton(TEST_DATA_PATH_ANNOTATION_QUALIFIED_NAME)), UAnnotation.class); if (annotation != null) { - final PsiAnnotationMemberValue value = annotation.findAttributeValue(PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME); - if (value instanceof PsiExpression) { - final Project project = value.getProject(); - final PsiConstantEvaluationHelper evaluationHelper = JavaPsiFacade.getInstance(project).getConstantEvaluationHelper(); - final Object constantValue = evaluationHelper.computeConstantExpression(value, false); + UExpression value = annotation.findAttributeValue(PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME); + if (value != null) { + final Project project = psiClass.getProject(); + final Object constantValue = value.evaluate(); if (constantValue instanceof String) { String path = (String) constantValue; if (path.contains(CONTENT_ROOT_VARIABLE)) { diff --git a/plugins/devkit/src/testAssistant/TestDataReferenceCollector.java b/plugins/devkit/src/testAssistant/TestDataReferenceCollector.java index 58c55749da4e..0e5cbdc456c5 100644 --- a/plugins/devkit/src/testAssistant/TestDataReferenceCollector.java +++ b/plugins/devkit/src/testAssistant/TestDataReferenceCollector.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,14 @@ import com.intellij.psi.*; import com.intellij.testFramework.PlatformTestUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.uast.*; +import org.jetbrains.uast.evaluation.SimpleEvaluatorExtension; +import org.jetbrains.uast.evaluation.UEvaluationContextKt; +import org.jetbrains.uast.values.UBooleanConstant; +import org.jetbrains.uast.values.UConstant; +import org.jetbrains.uast.values.UStringConstant; +import org.jetbrains.uast.values.UValue; +import org.jetbrains.uast.visitor.AbstractUastVisitor; import java.io.File; import java.util.*; @@ -61,22 +69,26 @@ public class TestDataReferenceCollector { @NotNull private List collectTestDataReferences(final PsiMethod method, - final Map> argumentMap, + final Map> argumentMap, final HashSet proceed) { final List result = new ArrayList<>(); if (myTestDataPath == null) { return result; } - method.accept(new JavaRecursiveElementVisitor() { + UMethod uMethod = (UMethod)UastContextKt.toUElement(method); + if (uMethod == null) { + return result; + } + uMethod.accept(new AbstractUastVisitor() { @Override - public void visitMethodCallExpression(PsiMethodCallExpression expression) { - String callText = expression.getMethodExpression().getReferenceName(); - if (callText == null) return; - PsiMethod callee = expression.resolveMethod(); + public boolean visitCallExpression(UCallExpression expression) { + String callText = expression.getMethodName(); + if (callText == null) return true; + UMethod callee = UastContextKt.toUElement(expression.resolve(), UMethod.class); if (callee != null && callee.hasModifierProperty(PsiModifier.ABSTRACT)) { final PsiClass calleeContainingClass = callee.getContainingClass(); if (calleeContainingClass != null && myContainingClass.isInheritor(calleeContainingClass, true)) { - final PsiMethod implementation = myContainingClass.findMethodBySignature(callee, true); + final UMethod implementation = UastContextKt.toUElement(myContainingClass.findMethodBySignature(callee, true), UMethod.class); if (implementation != null) { callee = implementation; } @@ -94,91 +106,78 @@ public class TestDataReferenceCollector { haveAnnotatedParameters = true; } } - if (expression.getMethodExpression().getQualifierExpression() == null && !haveAnnotatedParameters) { + if (expression.getReceiver() == null && !haveAnnotatedParameters) { result.addAll(collectTestDataReferences(callee, buildArgumentMap(expression, callee), proceed)); } } + return true; } }); return result; } - private void processCallArgument(PsiMethodCallExpression expression, Map> argumentMap, List result, final int index) { - final PsiExpression[] arguments = expression.getArgumentList().getExpressions(); - if (arguments.length > index) { - String testDataFile = evaluate(arguments [index], argumentMap); - if (testDataFile != null) { - result.add(myTestDataPath + testDataFile); + private void processCallArgument(UCallExpression expression, + Map> argumentMap, + List result, + final int index) { + final List arguments = expression.getValueArguments(); + if (arguments.size() > index) { + UValue testDataFileValue = UEvaluationContextKt.uValueOf(arguments.get(index), new TestDataEvaluatorExtension(argumentMap)); + if (testDataFileValue instanceof UStringConstant) { + result.add(myTestDataPath + ((UStringConstant) testDataFileValue).getValue()); } } } - private Map> buildArgumentMap(PsiMethodCallExpression expression, PsiMethod method) { - Map> result = new HashMap<>(); + private Map> buildArgumentMap(UCallExpression expression, PsiMethod method) { + Map> result = new HashMap<>(); final PsiParameter[] parameters = method.getParameterList().getParameters(); - final PsiExpression[] arguments = expression.getArgumentList().getExpressions(); - for (int i = 0; i < arguments.length && i < parameters.length; i++) { + final List arguments = expression.getValueArguments(); + for (int i = 0; i < arguments.size() && i < parameters.length; i++) { final int finalI = i; result.put(parameters [i].getName(), - (NullableComputable)() -> evaluate(arguments [finalI], Collections.>emptyMap())); + (NullableComputable)() -> UEvaluationContextKt.uValueOf(arguments.get(finalI), + new TestDataEvaluatorExtension(Collections.emptyMap()))); } return result; } - @Nullable - private String evaluate(PsiExpression expression, Map> arguments) { - if (expression instanceof PsiPolyadicExpression) { - PsiPolyadicExpression binaryExpression = (PsiPolyadicExpression)expression; - if (binaryExpression.getOperationTokenType() == JavaTokenType.PLUS) { - String r = ""; - for (PsiExpression op : binaryExpression.getOperands()) { - String lhs = evaluate(op, arguments); - if (lhs == null) return null; - r += lhs; - } - return r; - } - } - else if (expression instanceof PsiLiteralExpression) { - final Object value = ((PsiLiteralExpression)expression).getValue(); - if (value instanceof String) { - return (String) value; - } - } - else if (expression instanceof PsiReferenceExpression) { - final PsiElement result = ((PsiReferenceExpression)expression).resolve(); - if (result instanceof PsiParameter) { - final String name = ((PsiParameter)result).getName(); - final Computable arg = arguments.get(name); - return arg == null ? null : arg.compute(); - } - if (result instanceof PsiVariable) { - final PsiExpression initializer = ((PsiVariable)result).getInitializer(); - if (initializer != null) { - return evaluate(initializer, arguments); - } - } - } - else if (expression instanceof PsiMethodCallExpression) { - final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expression; - final String callText = methodCall.getMethodExpression().getText(); - if (callText.equals("getTestName")) { - final PsiExpression[] psiExpressions = methodCall.getArgumentList().getExpressions(); - if (psiExpressions.length == 1) { - if ("true".equals(psiExpressions[0].getText()) && !StringUtil.isEmpty(myTestName)) { - return PlatformTestUtil.lowercaseFirstLetter(myTestName, true); - } - return myTestName; - } - } - } - if (expression != null) { - myLogMessages.add("Failed to evaluate " + expression.getText()); - } - return null; - } - public String getLog() { return StringUtil.join(myLogMessages, "\n"); } + + private class TestDataEvaluatorExtension extends SimpleEvaluatorExtension { + private final Map> myArguments; + + private TestDataEvaluatorExtension(Map> arguments) { + myArguments = arguments; + } + + @Override + public Object evaluateMethodCall(PsiMethod target, List argumentValues) { + if (target.getName().equals("getTestName") && argumentValues.size() == 1) { + UValue lowercaseArg = argumentValues.get(0); + boolean lowercaseArgValue = lowercaseArg instanceof UBooleanConstant && ((UBooleanConstant) lowercaseArg).getValue(); + if (lowercaseArgValue && !StringUtil.isEmpty(myTestName)) { + return PlatformTestUtil.lowercaseFirstLetter(myTestName, true); + } + return myTestName; + + } + return super.evaluateMethodCall(target, argumentValues); + } + + @Override + public Object evaluateVariable(UVariable variable) { + if (variable instanceof UParameter) { + Computable value = myArguments.get(variable.getName()); + if (value != null) { + UValue computedValue = value.compute(); + UConstant constant = computedValue.toConstant(); + return constant != null ? constant.getValue() : super.evaluateVariable(variable); + } + } + return super.evaluateVariable(variable); + } + } } diff --git a/plugins/devkit/testData/referenceCollector/AbstractMethod.java b/plugins/devkit/testData/referenceCollector/AbstractMethod.java index 58bd43af2a80..371a852f380e 100644 --- a/plugins/devkit/testData/referenceCollector/AbstractMethod.java +++ b/plugins/devkit/testData/referenceCollector/AbstractMethod.java @@ -17,4 +17,8 @@ class ATest extends AbstractClass { private void configureByFile(@com.intellij.testFramework.TestDataFile String file) { } + + private String getTestName(boolean toUpperCase) { + return null; + } } \ No newline at end of file diff --git a/plugins/devkit/testData/referenceCollector/DoTestParameters.java b/plugins/devkit/testData/referenceCollector/DoTestParameters.java index c591ea3e31bc..d70ca597091a 100644 --- a/plugins/devkit/testData/referenceCollector/DoTestParameters.java +++ b/plugins/devkit/testData/referenceCollector/DoTestParameters.java @@ -14,4 +14,8 @@ public class ATest extends LightCodeInsightFixtureTestCase { private void configureByFile(@com.intellij.testFramework.TestDataFile String file) { } + + private String getTestName(boolean toUpperCase) { + return null; + } } diff --git a/plugins/devkit/testData/referenceCollector/TestNameAsParameter.java b/plugins/devkit/testData/referenceCollector/TestNameAsParameter.java index 0096df84e175..b80f293838bd 100644 --- a/plugins/devkit/testData/referenceCollector/TestNameAsParameter.java +++ b/plugins/devkit/testData/referenceCollector/TestNameAsParameter.java @@ -10,4 +10,8 @@ public class ATest extends LightCodeInsightFixtureTestCase { private void configureByFile(@com.intellij.testFramework.TestDataFile String file) { } + + private String getTestName(boolean toUpperCase) { + return null; + } } diff --git a/plugins/editorconfig/src/org/editorconfig/plugincomponents/ConfigProjectComponent.java b/plugins/editorconfig/src/org/editorconfig/plugincomponents/ConfigProjectComponent.java index b43bc8ba4554..aee42fb0422f 100644 --- a/plugins/editorconfig/src/org/editorconfig/plugincomponents/ConfigProjectComponent.java +++ b/plugins/editorconfig/src/org/editorconfig/plugincomponents/ConfigProjectComponent.java @@ -11,8 +11,8 @@ import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.startup.StartupActivity; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; +import com.intellij.openapi.vfs.VirtualFileListener; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.util.messages.MessageBus; import org.editorconfig.configmanagement.EditorSettingsManager; @@ -34,7 +34,7 @@ public class ConfigProjectComponent implements StartupActivity, DumbAware { bus.connect().subscribe(AppTopics.FILE_DOCUMENT_SYNC, encodingManager); bus.connect().subscribe(AppTopics.FILE_DOCUMENT_SYNC, editorSettingsManager); bus.connect().subscribe(AppTopics.FILE_DOCUMENT_SYNC, lineEndingsManager); - VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() { + VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileListener() { @Override public void fileCreated(@NotNull VirtualFileEvent event) { updateOpenEditors(event); diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GroovyDslFileIndex.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GroovyDslFileIndex.java index c8e4b13878ba..1146b82064e2 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GroovyDslFileIndex.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GroovyDslFileIndex.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,8 +28,8 @@ import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; +import com.intellij.openapi.vfs.VirtualFileListener; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.psi.*; import com.intellij.psi.impl.PsiModificationTrackerImpl; @@ -85,7 +85,7 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { private final MyDataIndexer myDataIndexer = new MyDataIndexer(); public GroovyDslFileIndex() { - VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() { + VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileListener() { @Override public void contentsChanged(@NotNull VirtualFileEvent event) { final VirtualFile file = event.getFile(); @@ -354,7 +354,7 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { private static List getDslScripts(final Project project) { return CachedValuesManager.getManager(project).getCachedValue(project, SCRIPTS_CACHE, () -> { if (GdslUtil.ourGdslStopped) { - return CachedValueProvider.Result.create(Collections.emptyList(), ModificationTracker.NEVER_CHANGED); + return CachedValueProvider.Result.create(Collections.emptyList(), ModificationTracker.NEVER_CHANGED); } // eagerly initialize some services used by background gdsl parsing threads diff --git a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GreclipseBuilder.java b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GreclipseBuilder.java index 01a53ffdf0a6..8fd0a18f1a40 100644 --- a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GreclipseBuilder.java +++ b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GreclipseBuilder.java @@ -117,7 +117,7 @@ public class GreclipseBuilder extends ModuleLevelBuilder { if (!useGreclipse(context)) return ModuleLevelBuilder.ExitCode.NOTHING_DONE; try { - final List toCompile = myHelper.collectChangedFiles(context, dirtyFilesHolder, false, true, Ref.create(false)); + List toCompile = myHelper.collectChangedFiles(context, dirtyFilesHolder, false, Ref.create(false)); if (toCompile.isEmpty()) { return ExitCode.NOTHING_DONE; } diff --git a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovycOutputParser.java b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovycOutputParser.java index 36bebb7eb622..4c10de3099e1 100644 --- a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovycOutputParser.java +++ b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovycOutputParser.java @@ -21,7 +21,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/JpsGroovycRunner.java b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/JpsGroovycRunner.java index 767528729bff..9e261cb37686 100644 --- a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/JpsGroovycRunner.java +++ b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/JpsGroovycRunner.java @@ -23,6 +23,7 @@ import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; @@ -36,11 +37,8 @@ import org.jetbrains.jps.builders.FileProcessor; import org.jetbrains.jps.builders.java.JavaBuilderUtil; import org.jetbrains.jps.builders.java.dependencyView.Callbacks; import org.jetbrains.jps.builders.storage.SourceToOutputMapping; -import org.jetbrains.jps.incremental.Builder; -import org.jetbrains.jps.incremental.CompileContext; +import org.jetbrains.jps.incremental.*; import org.jetbrains.jps.incremental.ModuleLevelBuilder.ExitCode; -import org.jetbrains.jps.incremental.ProjectBuildException; -import org.jetbrains.jps.incremental.Utils; import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.messages.CompilerMessage; import org.jetbrains.jps.model.JpsDummyElement; @@ -72,12 +70,13 @@ public abstract class JpsGroovycRunner dirtyFilesHolder, Builder builder, GroovyOutputConsumer outputConsumer) throws ProjectBuildException { + List messages; long start = 0; try { JpsGroovySettings settings = JpsGroovySettings.getSettings(context.getProjectDescriptor().getProject()); Ref hasStubExcludes = Ref.create(false); - final List toCompile = collectChangedFiles(context, dirtyFilesHolder, myForStubs, false, hasStubExcludes); + final List toCompile = collectChangedFiles(context, dirtyFilesHolder, myForStubs, hasStubExcludes); if (toCompile.isEmpty()) { return ExitCode.NOTHING_DONE; } @@ -104,7 +103,8 @@ public abstract class JpsGroovycRunner message.getKind() == BuildMessage.Kind.ERROR)) { + throw new StopBuildException(); + } + + return ExitCode.OK; } protected void stubsGenerated(CompileContext context, Map generationOutputs, MultiMap compiled) { @@ -294,7 +299,7 @@ public abstract class JpsGroovycRunner collectChangedFiles(CompileContext context, DirtyFilesHolder dirtyFilesHolder, - final boolean forStubs, final boolean forEclipse, final Ref hasExcludes) + boolean forStubs, Ref hasExcludes) throws IOException { final JpsJavaCompilerConfiguration configuration = diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GrMainCompletionProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GrMainCompletionProvider.java index 07a98b18b1b3..3f4c36dc5f4d 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GrMainCompletionProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GrMainCompletionProvider.java @@ -18,7 +18,6 @@ package org.jetbrains.plugins.groovy.lang.completion; import com.intellij.codeInsight.completion.*; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.EmptyRunnable; import com.intellij.openapi.util.text.StringUtil; import com.intellij.patterns.ElementPattern; @@ -31,7 +30,6 @@ import com.intellij.psi.impl.light.LightElement; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.Consumer; -import com.intellij.util.PairConsumer; import com.intellij.util.ProcessingContext; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -458,7 +456,7 @@ public class GrMainCompletionProvider extends CompletionProvider KEY = Key.create("fxml.presence.modification.tracker"); private final SimpleModificationTracker myModificationTracker; diff --git a/plugins/junit/src/com/intellij/execution/junit/TestsPattern.java b/plugins/junit/src/com/intellij/execution/junit/TestsPattern.java index 261d8172205b..640cec1a0bf0 100644 --- a/plugins/junit/src/com/intellij/execution/junit/TestsPattern.java +++ b/plugins/junit/src/com/intellij/execution/junit/TestsPattern.java @@ -72,15 +72,13 @@ public class TestsPattern extends TestPackage { if (classNames.size() == data.getPatterns().size()) { return new SearchForTestsTask(project, myServerSocket) { @Override - protected void search() throws ExecutionException { - final Function nameFunction = StringUtil.isEmpty(data.METHOD_NAME) - ? FunctionUtil.id() - : (Function)className -> className; - addClassesListToJavaParameters(classNames, nameFunction, "", false, getJavaParameters()); - } + protected void search() throws ExecutionException { } @Override - protected void onFound() {} + protected void onFound() throws ExecutionException { + final Function nameFunction = StringUtil.isEmpty(data.METHOD_NAME) ? FunctionUtil.id() : className -> className; + addClassesListToJavaParameters(classNames, nameFunction, "", false, getJavaParameters()); + } }; } diff --git a/plugins/maven/maven3-server-common/src/org/jetbrains/idea/maven/server/embedder/CustomMaven3ModelInterpolator.java b/plugins/maven/maven3-server-common/src/org/jetbrains/idea/maven/server/embedder/CustomMaven3ModelInterpolator.java index 07ae15ca110c..3a13c7408842 100644 --- a/plugins/maven/maven3-server-common/src/org/jetbrains/idea/maven/server/embedder/CustomMaven3ModelInterpolator.java +++ b/plugins/maven/maven3-server-common/src/org/jetbrains/idea/maven/server/embedder/CustomMaven3ModelInterpolator.java @@ -15,6 +15,7 @@ */ package org.jetbrains.idea.maven.server.embedder; +import com.intellij.util.containers.ContainerUtil; import org.apache.maven.model.Model; import org.apache.maven.project.ProjectBuilderConfiguration; import org.apache.maven.project.interpolation.ModelInterpolationException; @@ -31,12 +32,21 @@ public class CustomMaven3ModelInterpolator extends StringSearchModelInterpolator super(pathTranslator); } + @Override + public Model interpolate(Model model, File projectDir, ProjectBuilderConfiguration config, boolean debugEnabled) + throws ModelInterpolationException { + this.interpolateObject(ContainerUtil.ar(model.getParent(), model), model, projectDir, config, debugEnabled); + return model; + } + @Override protected void interpolateObject(Object obj, Model model, File projectDir, ProjectBuilderConfiguration config, boolean debugEnabled) throws ModelInterpolationException { + if (obj == null) return; + // IDEA-74131 avoid concurrent access to the static cache in StringSearchModelInterpolator synchronized (CustomMaven3ModelInterpolator.class) { try { diff --git a/plugins/maven/maven3-server-common/src/org/jetbrains/idea/maven/server/embedder/CustomMaven3ModelInterpolator2.java b/plugins/maven/maven3-server-common/src/org/jetbrains/idea/maven/server/embedder/CustomMaven3ModelInterpolator2.java index acace162b2fe..16f9f4667c11 100644 --- a/plugins/maven/maven3-server-common/src/org/jetbrains/idea/maven/server/embedder/CustomMaven3ModelInterpolator2.java +++ b/plugins/maven/maven3-server-common/src/org/jetbrains/idea/maven/server/embedder/CustomMaven3ModelInterpolator2.java @@ -35,6 +35,11 @@ public class CustomMaven3ModelInterpolator2 extends StringSearchModelInterpolato private String localRepository; + @Override + public void interpolateObject(Object obj, Model model, File projectDir, ModelBuildingRequest config, ModelProblemCollector problems) { + super.interpolateObject(obj, model, projectDir, config, problems); + } + @Override protected List createValueSources(Model model, File projectDir, diff --git a/plugins/maven/maven3-server-common/src/org/jetbrains/idea/maven/server/embedder/CustomModelValidator.java b/plugins/maven/maven3-server-common/src/org/jetbrains/idea/maven/server/embedder/CustomModelValidator.java new file mode 100644 index 000000000000..aa9b5abfd339 --- /dev/null +++ b/plugins/maven/maven3-server-common/src/org/jetbrains/idea/maven/server/embedder/CustomModelValidator.java @@ -0,0 +1,102 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.maven.server.embedder; + +import org.apache.maven.model.InputLocation; +import org.apache.maven.model.Model; +import org.apache.maven.model.Parent; +import org.apache.maven.model.building.ModelBuildingRequest; +import org.apache.maven.model.building.ModelProblem; +import org.apache.maven.model.building.ModelProblemCollector; +import org.apache.maven.model.interpolation.ModelInterpolator; +import org.apache.maven.model.validation.DefaultModelValidator; +import org.apache.maven.model.validation.ModelValidator; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; + +import static com.intellij.openapi.util.text.StringUtil.notNullize; + +/** + * @author ibessonov + */ +@Component(role = ModelValidator.class, hint = "ide") +public class CustomModelValidator extends DefaultModelValidator { + + @Requirement(role = ModelInterpolator.class) + private CustomMaven3ModelInterpolator2 myModelInterpolator; + + @Override + public void validateRawModel(Model model, ModelBuildingRequest request, ModelProblemCollector problems) { + ProxyModelProblemCollector problemsProxy = new ProxyModelProblemCollector(problems); + super.validateRawModel(model, request, problemsProxy); + + Parent parent = model.getParent(); + if (parent != null && !problemsProxy.hasFatalErrors()) { + if (parent.getGroupId().contains("${") || parent.getArtifactId().contains("${") || parent.getVersion().contains("${")) { + myModelInterpolator.interpolateObject(parent, model, model.getProjectDirectory(), request, problems); + + if (equals(parent.getGroupId(), model.getGroupId()) && equals(parent.getArtifactId(), model.getArtifactId())) { + try { + // will encounter validation problem right on the start + super.validateRawModel(model, request, new AbortOnFirstErrorProblemsCollector(problems)); + } + catch (AbortOnErrorException ignored) { + } + } + } + } + } + + private static boolean equals(String a, String b) { + return notNullize(a).equals(notNullize(b)); + } + + private static class ProxyModelProblemCollector implements ModelProblemCollector { + + private final ModelProblemCollector myDelegate; + private boolean myHasFatalErrors = false; + + public ProxyModelProblemCollector(ModelProblemCollector delegate) { + myDelegate = delegate; + } + + @Override + public void add(ModelProblem.Severity severity, String s, InputLocation location, Exception e) { + myHasFatalErrors |= (severity == ModelProblem.Severity.FATAL); + myDelegate.add(severity, s, location, e); + } + + public boolean hasFatalErrors() { + return myHasFatalErrors; + } + } + + private static class AbortOnErrorException extends RuntimeException { + } + + private static class AbortOnFirstErrorProblemsCollector extends ProxyModelProblemCollector { + + public AbortOnFirstErrorProblemsCollector(ModelProblemCollector delegate) { + super(delegate); + } + + @Override + public void add(ModelProblem.Severity severity, String s, InputLocation location, Exception e) { + super.add(severity, s, location, e); + throw new AbortOnErrorException(); + } + } +} diff --git a/plugins/maven/maven3-server-impl/src/META-INF/plexus/components.xml b/plugins/maven/maven3-server-impl/src/META-INF/plexus/components.xml index fd5911c48a92..2f75bed40b0e 100644 --- a/plugins/maven/maven3-server-impl/src/META-INF/plexus/components.xml +++ b/plugins/maven/maven3-server-impl/src/META-INF/plexus/components.xml @@ -111,6 +111,13 @@ false + + org.apache.maven.model.validation.ModelValidator + ide + org.jetbrains.idea.maven.server.embedder.CustomModelValidator + + false + diff --git a/plugins/maven/maven3-server-impl/src/org/jetbrains/idea/maven/server/Maven3ServerEmbedderImpl.java b/plugins/maven/maven3-server-impl/src/org/jetbrains/idea/maven/server/Maven3ServerEmbedderImpl.java index 3cb76ab5e162..78968cbc9041 100644 --- a/plugins/maven/maven3-server-impl/src/org/jetbrains/idea/maven/server/Maven3ServerEmbedderImpl.java +++ b/plugins/maven/maven3-server-impl/src/org/jetbrains/idea/maven/server/Maven3ServerEmbedderImpl.java @@ -43,12 +43,11 @@ import org.apache.maven.model.Activation; import org.apache.maven.model.Model; import org.apache.maven.model.Plugin; import org.apache.maven.model.Profile; -import org.apache.maven.model.building.FileModelSource; -import org.apache.maven.model.building.ModelProblem; -import org.apache.maven.model.building.ModelProcessor; +import org.apache.maven.model.building.*; import org.apache.maven.model.interpolation.ModelInterpolator; import org.apache.maven.model.io.ModelReader; import org.apache.maven.model.profile.DefaultProfileInjector; +import org.apache.maven.model.validation.ModelValidator; import org.apache.maven.plugin.LegacySupport; import org.apache.maven.plugin.PluginDescriptorCache; import org.apache.maven.plugin.internal.PluginDependenciesResolver; @@ -117,6 +116,7 @@ import java.util.*; *

* maven-model-builder: * org.jetbrains.idea.maven.server.embedder.CustomMaven3ModelInterpolator2 <-> org.apache.maven.model.interpolation.StringSearchModelInterpolator + * org.jetbrains.idea.maven.server.embedder.CustomModelValidator <-> org.apache.maven.model.validation.ModelValidator */ public class Maven3ServerEmbedderImpl extends Maven3ServerEmbedder { @@ -552,9 +552,16 @@ public class Maven3ServerEmbedderImpl extends Maven3ServerEmbedder { myContainer.addComponent(getComponent(ArtifactResolver.class, "ide"), ArtifactResolver.ROLE); myContainer.addComponent(getComponent(RepositoryMetadataManager.class, "ide"), RepositoryMetadataManager.class.getName()); myContainer.addComponent(getComponent(PluginDescriptorCache.class, "ide"), PluginDescriptorCache.class.getName()); - myContainer.addComponent(getComponent(ModelInterpolator.class, "ide"), ModelInterpolator.class.getName()); + ModelInterpolator modelInterpolator = getComponent(ModelInterpolator.class, "ide"); + myContainer.addComponent(modelInterpolator, ModelInterpolator.class.getName()); myContainer.addComponent(getComponent(org.apache.maven.project.interpolation.ModelInterpolator.class, "ide"), org.apache.maven.project.interpolation.ModelInterpolator.ROLE); + ModelValidator modelValidator = getComponent(ModelValidator.class, "ide"); + myContainer.addComponent(modelValidator, ModelValidator.class.getName()); + + DefaultModelBuilder defaultModelBuilder = (DefaultModelBuilder)getComponent(ModelBuilder.class); + defaultModelBuilder.setModelValidator(modelValidator); + defaultModelBuilder.setModelInterpolator(modelInterpolator); } private void setConsoleAndIndicator(MavenServerConsole console, MavenServerProgressIndicator indicator) { diff --git a/plugins/maven/maven30-server-impl/src/META-INF/plexus/components.xml b/plugins/maven/maven30-server-impl/src/META-INF/plexus/components.xml index b4b465e64bd4..8bb7092e2dbd 100644 --- a/plugins/maven/maven30-server-impl/src/META-INF/plexus/components.xml +++ b/plugins/maven/maven30-server-impl/src/META-INF/plexus/components.xml @@ -111,6 +111,13 @@ false + + org.apache.maven.model.validation.ModelValidator + ide + org.jetbrains.idea.maven.server.embedder.CustomModelValidator + + false + diff --git a/plugins/maven/maven30-server-impl/src/org/jetbrains/idea/maven/server/Maven30ServerEmbedderImpl.java b/plugins/maven/maven30-server-impl/src/org/jetbrains/idea/maven/server/Maven30ServerEmbedderImpl.java index 5f8c6d06add8..5d7a4d618efb 100644 --- a/plugins/maven/maven30-server-impl/src/org/jetbrains/idea/maven/server/Maven30ServerEmbedderImpl.java +++ b/plugins/maven/maven30-server-impl/src/org/jetbrains/idea/maven/server/Maven30ServerEmbedderImpl.java @@ -39,9 +39,12 @@ import org.apache.maven.model.Activation; import org.apache.maven.model.Model; import org.apache.maven.model.Plugin; import org.apache.maven.model.Profile; +import org.apache.maven.model.building.DefaultModelBuilder; +import org.apache.maven.model.building.ModelBuilder; import org.apache.maven.model.building.ModelProblem; import org.apache.maven.model.interpolation.ModelInterpolator; import org.apache.maven.model.profile.DefaultProfileInjector; +import org.apache.maven.model.validation.ModelValidator; import org.apache.maven.plugin.LegacySupport; import org.apache.maven.plugin.PluginDescriptorCache; import org.apache.maven.plugin.internal.PluginDependenciesResolver; @@ -104,6 +107,7 @@ import java.util.*; *

* maven-model-builder: * org.jetbrains.idea.maven.server.embedder.CustomMaven3ModelInterpolator2 <-> org.apache.maven.model.interpolation.StringSearchModelInterpolator + * org.jetbrains.idea.maven.server.embedder.CustomModelValidator <-> org.apache.maven.model.validation.ModelValidator */ public class Maven30ServerEmbedderImpl extends Maven3ServerEmbedder { @@ -516,9 +520,16 @@ public class Maven30ServerEmbedderImpl extends Maven3ServerEmbedder { myContainer.addComponent(getComponent(ArtifactResolver.class, "ide"), ArtifactResolver.ROLE); myContainer.addComponent(getComponent(RepositoryMetadataManager.class, "ide"), RepositoryMetadataManager.class.getName()); myContainer.addComponent(getComponent(PluginDescriptorCache.class, "ide"), PluginDescriptorCache.class.getName()); - myContainer.addComponent(getComponent(ModelInterpolator.class, "ide"), ModelInterpolator.class.getName()); + ModelInterpolator modelInterpolator = getComponent(ModelInterpolator.class, "ide"); + myContainer.addComponent(modelInterpolator, ModelInterpolator.class.getName()); myContainer.addComponent(getComponent(org.apache.maven.project.interpolation.ModelInterpolator.class, "ide"), org.apache.maven.project.interpolation.ModelInterpolator.ROLE); + ModelValidator modelValidator = getComponent(ModelValidator.class, "ide"); + myContainer.addComponent(modelValidator, ModelValidator.class.getName()); + + DefaultModelBuilder defaultModelBuilder = (DefaultModelBuilder)getComponent(ModelBuilder.class); + defaultModelBuilder.setModelValidator(modelValidator); + defaultModelBuilder.setModelInterpolator(modelInterpolator); } private void setConsoleAndIndicator(MavenServerConsole console, MavenServerProgressIndicator indicator) { diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginDomUtil.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginDomUtil.java index fc54abc7bd59..444219f26077 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginDomUtil.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginDomUtil.java @@ -16,21 +16,39 @@ package org.jetbrains.idea.maven.dom; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; +import com.intellij.psi.xml.XmlElement; import com.intellij.util.xml.DomElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.maven.dom.model.MavenDomConfiguration; import org.jetbrains.idea.maven.dom.model.MavenDomPlugin; import org.jetbrains.idea.maven.dom.plugin.MavenDomPluginModel; +import org.jetbrains.idea.maven.model.MavenId; +import org.jetbrains.idea.maven.model.MavenPlugin; +import org.jetbrains.idea.maven.project.MavenProject; import org.jetbrains.idea.maven.project.MavenProjectsManager; import org.jetbrains.idea.maven.utils.MavenArtifactUtil; import java.io.File; public class MavenPluginDomUtil { + + @Nullable + public static MavenProject findMavenProject(@NotNull DomElement domElement) { + XmlElement xmlElement = domElement.getXmlElement(); + if (xmlElement == null) return null; + PsiFile psiFile = xmlElement.getContainingFile(); + if (psiFile == null) return null; + VirtualFile file = psiFile.getVirtualFile(); + if (file == null) return null; + return MavenProjectsManager.getInstance(psiFile.getProject()).findProject(file); + } + @Nullable public static MavenDomPluginModel getMavenPluginModel(DomElement element) { Project project = element.getManager().getProject(); @@ -41,7 +59,18 @@ public class MavenPluginDomUtil { String groupId = pluginElement.getGroupId().getStringValue(); String artifactId = pluginElement.getArtifactId().getStringValue(); String version = pluginElement.getVersion().getStringValue(); - + if (StringUtil.isEmpty(version)) { + MavenProject mavenProject = findMavenProject(element); + if (mavenProject != null) { + for (MavenPlugin plugin : mavenProject.getPlugins()) { + if (MavenArtifactUtil.isPluginIdEquals(groupId, artifactId, plugin.getGroupId(), plugin.getArtifactId())) { + MavenId pluginMavenId = plugin.getMavenId(); + version = pluginMavenId.getVersion(); + break; + } + } + } + } return getMavenPluginModel(project, groupId, artifactId, version); } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/inspections/MavenPropertyInParentInspection.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/inspections/MavenPropertyInParentInspection.java index dcc460cd793d..079ea338710b 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/inspections/MavenPropertyInParentInspection.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/inspections/MavenPropertyInParentInspection.java @@ -19,7 +19,9 @@ import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeInspection.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.psi.xml.XmlElement; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.intellij.util.containers.ContainerUtil; @@ -31,9 +33,14 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.maven.dom.MavenDomBundle; import org.jetbrains.idea.maven.dom.model.MavenDomParent; import org.jetbrains.idea.maven.dom.model.MavenDomProjectModel; +import org.jetbrains.idea.maven.dom.references.MavenPropertyPsiReference; +import org.jetbrains.idea.maven.dom.references.MavenPsiElementWrapper; import java.util.List; +import static com.intellij.openapi.util.text.StringUtil.isEmpty; +import static com.intellij.util.containers.ContainerUtil.findInstance; + /** * @author ibessonov */ @@ -86,12 +93,17 @@ public class MavenPropertyInParentInspection extends XmlSuppressableInspectionTo String unresolvedValue = domValue.getRawText(); if (unresolvedValue != null && unresolvedValue.contains("${")) { LocalQuickFix fix = null; - String resolvedValue = domValue.getValue(); - if (!unresolvedValue.equals(resolvedValue)) { + String resolvedValue = domValue.getStringValue(); + if (unresolvedValue.equals(resolvedValue)) { + resolvedValue = resolveXmlElement(domValue.getXmlElement()); + } + + if (!unresolvedValue.equals(resolvedValue) && !isEmpty(resolvedValue)) { + String finalResolvedValue = resolvedValue; fix = new LocalQuickFixBase(MavenDomBundle.message("refactoring.inline.property")) { @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - ((XmlTag)descriptor.getPsiElement()).getValue().setText(resolvedValue); + ((XmlTag)descriptor.getPsiElement()).getValue().setText(finalResolvedValue); } }; } @@ -99,4 +111,20 @@ public class MavenPropertyInParentInspection extends XmlSuppressableInspectionTo fix, ProblemHighlightType.GENERIC_ERROR, isOnTheFly)); } } + + @Nullable + private static String resolveXmlElement(@Nullable XmlElement xmlElement) { + if (xmlElement == null) return null; + + MavenPropertyPsiReference psiReference = findInstance(xmlElement.getReferences(), MavenPropertyPsiReference.class); + if (psiReference == null) return null; + + PsiElement resolvedElement = psiReference.resolve(); + if (resolvedElement == null || !(resolvedElement instanceof MavenPsiElementWrapper)) return null; + + PsiElement xmlTag = ((MavenPsiElementWrapper)resolvedElement).getWrappee(); + if (!(xmlTag instanceof XmlTag)) return null; + + return ((XmlTag)xmlTag).getValue().getTrimmedText(); + } } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProject.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProject.java index da6361a88896..83e42fcf169d 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProject.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProject.java @@ -125,6 +125,9 @@ public class MavenProject { MavenModel model = readerResult.mavenModel; newState.myMavenId = model.getMavenId(); + if (model.getParent() != null) { + newState.myParentId = model.getParent().getMavenId(); + } newState.myPackaging = model.getPackaging(); newState.myName = model.getName(); @@ -144,14 +147,6 @@ public class MavenProject { doSetResolvedAttributes(newState, readerResult, resetArtifacts); MavenModelPropertiesPatcher.patch(newState.myProperties, newState.myPlugins); - if (model.getParent() != null) { - MavenId parentId = model.getParent().getMavenId(); - newState.myParentId = new MavenId( - MavenUtil.expandProperties(parentId.getGroupId(), newState.myProperties), - MavenUtil.expandProperties(parentId.getArtifactId(), newState.myProperties), - MavenUtil.expandProperties(parentId.getVersion(), newState.myProperties) - ); - } newState.myModulesPathsAndNames = collectModulePathsAndNames(model, getDirectory()); Collection newProfiles = collectProfilesIds(model.getProfiles()); diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/importing/ReimportingTest.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/importing/ReimportingTest.java index 4ea6a3026ee3..f9c1bf732ca8 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/importing/ReimportingTest.java +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/importing/ReimportingTest.java @@ -402,4 +402,51 @@ public class ReimportingTest extends MavenImportingTestCase { assertEquals("1.7", compilerConfiguration.getBytecodeTargetLevel(getModule("project"))); assertEquals("1.7", compilerConfiguration.getBytecodeTargetLevel(getModule("m1"))); } + + public void testParentVersionProperty2() throws Exception { + createProjectPom("test\n" + + "project\n" + + "1\n" + + "\n" + + " m1\n" + + ""); + + String m1pomTemplate = "\n" + + " ${my.parent.groupId}\n" + + " project\n" + + " ${my.parent.version}\n" + + "\n" + + "m1\n" + + "${my.parent.version}\n" + + "\n" + + " 1\n" + + " test\n" + + "\n" + + "\n" + + " \n" + + " \n" + + " maven-compiler-plugin\n" + + " 3.1\n" + + " \n" + + " %s\n" + + " %\n" + + " \n" + + " \n" + + " \n" + + ""; + createModulePom("m1", String.format(m1pomTemplate, "1.8")); + + CompilerConfiguration compilerConfiguration = CompilerConfiguration.getInstance(myProject); + + configConfirmationForYesAnswer(); + importProjectWithMaven3(); + assertEquals(LanguageLevel.JDK_1_8, getEffectiveLanguageLevel(getModule("m1"))); + assertEquals("1.8", compilerConfiguration.getBytecodeTargetLevel(getModule("m1"))); + + createModulePom("m1", String.format(m1pomTemplate, "1.7")); + + importProjectWithMaven3(); + assertEquals(LanguageLevel.JDK_1_7, getEffectiveLanguageLevel(getModule("m1"))); + assertEquals("1.7", compilerConfiguration.getBytecodeTargetLevel(getModule("m1"))); + } } diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditorFileListener.java b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditorFileListener.java index 3abfdd41391d..39551c157464 100644 --- a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditorFileListener.java +++ b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditorFileListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,8 +28,8 @@ import com.intellij.openapi.progress.util.ReadTask; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.NotNullLazyValue; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; +import com.intellij.openapi.vfs.VirtualFileListener; import com.intellij.openapi.vfs.VirtualFilePropertyEvent; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.update.MergingUpdateQueue; @@ -46,7 +46,7 @@ import java.util.stream.Collectors; /** * @author Dmitry Batkovich */ -class ResourceBundleEditorFileListener extends VirtualFileAdapter { +class ResourceBundleEditorFileListener implements VirtualFileListener { private static final Update FORCE_UPDATE = new Update("FORCE_UPDATE") { @Override public void run() { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnEntriesFileListener.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnEntriesFileListener.java index 09b7abfbf2d0..ca0b2de0d60d 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnEntriesFileListener.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnEntriesFileListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,15 +20,15 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.changes.VcsAnnotationRefresher; import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; +import com.intellij.openapi.vfs.VirtualFileListener; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collections; import java.util.List; -public class SvnEntriesFileListener extends VirtualFileAdapter { +public class SvnEntriesFileListener implements VirtualFileListener { private final Project myProject; private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnEntriesFileListener"); private VcsDirtyScopeManager myDirtyScopeManager; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java index f8d5ae797dcc..db4c0d4c7710 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,8 @@ package org.jetbrains.idea.svn; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.command.CommandAdapter; import com.intellij.openapi.command.CommandEvent; +import com.intellij.openapi.command.CommandListener; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.undo.UndoManager; import com.intellij.openapi.diagnostic.Logger; @@ -65,7 +65,7 @@ import java.util.*; import static com.intellij.openapi.vfs.VfsUtilCore.virtualToIoFile; import static com.intellij.util.containers.ContainerUtil.map; -public class SvnFileSystemListener extends CommandAdapter implements LocalFileOperationsHandler, Disposable { +public class SvnFileSystemListener implements LocalFileOperationsHandler, Disposable, CommandListener { private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnFileSystemListener"); private final LocalFileSystem myLfs; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileUrlMappingImpl.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileUrlMappingImpl.java index 81f47347b236..2c227b2f6118 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileUrlMappingImpl.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileUrlMappingImpl.java @@ -17,7 +17,10 @@ package org.jetbrains.idea.svn; import com.intellij.lifecycle.PeriodicalTasksCloser; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.components.*; +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.components.StoragePathMacros; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.DumbAwareRunnable; @@ -42,7 +45,7 @@ import java.util.List; import java.util.Set; @State(name = "SvnFileUrlMappingImpl", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) -public class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentStateComponent, ProjectComponent { +public class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentStateComponent { private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnFileUrlMappingImpl"); private final SvnCompatibilityChecker myChecker; @@ -99,6 +102,7 @@ public class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentState myNestedCopiesHolder = new NestedCopiesHolder(); } + @Override @Nullable public SVNURL getUrlForFile(final File file) { final RootUrlInfo rootUrlInfo = getWcRootForFilePath(file); @@ -125,6 +129,7 @@ public class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentState } } + @Override @Nullable public File getLocalPath(@NotNull String url) { synchronized (myMonitor) { @@ -141,6 +146,7 @@ public class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentState } } + @Override @Nullable public RootUrlInfo getWcRootForFilePath(final File file) { synchronized (myMonitor) { @@ -153,12 +159,14 @@ public class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentState } } + @Override public boolean rootsDiffer() { synchronized (myMonitor) { return myMapping.isRootsDifferFromSettings(); } } + @Override @Nullable public RootUrlInfo getWcRootForUrl(final String url) { synchronized (myMonitor) { @@ -180,6 +188,7 @@ public class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentState * Returns real working copies roots - if there is -> Subversion setting, * and there is one working copy, will return one root */ + @Override public List getAllWcInfos() { synchronized (myMonitor) { // a copy is created inside @@ -194,6 +203,7 @@ public class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentState } } + @Override @NotNull public List convertRoots(@NotNull List result) { if (MyRootsHelper.isInProgress()) return ContainerUtil.newArrayList(result); @@ -316,17 +326,20 @@ public class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentState } } + @Override @NotNull public VirtualFile[] getNotFilteredRoots() { return myRootsHelper.execute(); } + @Override public boolean isEmpty() { synchronized (myMonitor) { return myMapping.isEmpty(); } } + @Override public SvnMappingSavedPart getState() { final SvnMappingSavedPart result = new SvnMappingSavedPart(); @@ -353,6 +366,7 @@ public class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentState return copy; } + @Override public void loadState(final SvnMappingSavedPart state) { ((ProjectLevelVcsManagerImpl) ProjectLevelVcsManager.getInstance(myProject)).addInitializationRequest( VcsInitObject.AFTER_COMMON, (DumbAwareRunnable)() -> ApplicationManager.getApplication().executeOnPooledThread(() -> { @@ -394,21 +408,4 @@ public class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentState mapping.add(info); } } - - public void projectOpened() { - } - - public void projectClosed() { - } - - @NotNull - public String getComponentName() { - return "SvnFileUrlMappingImpl"; - } - - public void initComponent() { - } - - public void disposeComponent() { - } } diff --git a/python/build.xml b/python/build.xml index 22f999027050..0669a300ee4e 100644 --- a/python/build.xml +++ b/python/build.xml @@ -38,7 +38,7 @@ diff --git a/python/educational-core/resources/META-INF/plugin.xml b/python/educational-core/resources/META-INF/plugin.xml index 1ff292d678d4..1f517fe20d7b 100644 --- a/python/educational-core/resources/META-INF/plugin.xml +++ b/python/educational-core/resources/META-INF/plugin.xml @@ -122,9 +122,9 @@ - - - + + + @@ -154,7 +154,7 @@ - + @@ -204,7 +204,6 @@ - \ No newline at end of file diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCCheckListener.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCCheckListener.java deleted file mode 100644 index 12aa92541496..000000000000 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCCheckListener.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.jetbrains.edu.coursecreator; - -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.jetbrains.edu.learning.StudyCheckListener; -import com.jetbrains.edu.learning.courseFormat.tasks.Task; -import org.jetbrains.annotations.NotNull; - -public class CCCheckListener implements StudyCheckListener { - - @Override - public void beforeCheck(@NotNull Project project, @NotNull Task task) { - if (!CCUtils.isCourseCreator(project)) { - return; - } - VirtualFile taskDir = task.getTaskDir(project); - if (taskDir == null) { - return; - } - CCUtils.updateResources(project, task, taskDir); - } -} diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java index f5c081335c40..61d0323f968c 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java @@ -1,7 +1,6 @@ package com.jetbrains.edu.coursecreator; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.PathManager; import com.intellij.openapi.components.AbstractProjectComponent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; @@ -15,12 +14,11 @@ import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.statistics.EduUsagesCollector; import org.jetbrains.annotations.NotNull; -import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -47,9 +45,6 @@ public class CCProjectComponent extends AbstractProjectComponent { CCProjectService.getInstance(myProject).setCourse(null); oldCourse.initCourse(true); oldCourse.setCourseMode(CCUtils.COURSE_MODE); - File coursesDir = new File(PathManager.getConfigPath(), "courses"); - File courseDir = new File(coursesDir, oldCourse.getName() + "-" + myProject.getName()); - oldCourse.setCourseDirectory(courseDir.getPath()); StudyUtils.registerStudyToolWindow(oldCourse, myProject); transformFiles(oldCourse, myProject); } diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCRefactoringElementListenerProvider.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCRefactoringElementListenerProvider.java index b9d46aa47d06..a917b185b31c 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCRefactoringElementListenerProvider.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCRefactoringElementListenerProvider.java @@ -15,10 +15,7 @@ */ package com.jetbrains.edu.coursecreator; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; @@ -30,16 +27,14 @@ import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.courseFormat.tasks.Task; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.IOException; import java.util.Map; public class CCRefactoringElementListenerProvider implements RefactoringElementListenerProvider { - private static final Logger LOG = Logger.getInstance(CCRefactoringElementListenerProvider.class); @Nullable @Override @@ -94,21 +89,8 @@ public class CCRefactoringElementListenerProvider implements RefactoringElementL if (taskFile == null) { return; } - ApplicationManager.getApplication().runWriteAction(() -> { - VirtualFile patternFile = StudyUtils.getPatternFile(taskFile, oldName); - if (patternFile != null) { - try { - patternFile.delete(CCRefactoringElementListenerProvider.class); - } - catch (IOException e) { - LOG.info(e); - } - } - }); - taskFiles.remove(oldName); taskFiles.put(StudyUtils.pathRelativeToTask(file.getVirtualFile()), taskFile); - CCUtils.createResourceFile(file.getVirtualFile(), course, taskDir.getVirtualFile()); } @Override diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCUtils.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCUtils.java index c12b680a292b..0b8815f4aa58 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCUtils.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCUtils.java @@ -8,34 +8,37 @@ import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.DumbModePermission; -import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootModificationUtil; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtilRt; -import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileVisitor; import com.intellij.psi.PsiDirectory; import com.intellij.util.Function; +import com.intellij.util.containers.hash.HashMap; import com.jetbrains.edu.learning.EduPluginConfigurator; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.StudyItem; import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.learning.courseFormat.tasks.Task; -import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks; +import org.apache.commons.codec.binary.Base64; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.File; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; public class CCUtils { public static final String ANSWER_EXTENSION_DOTTED = ".answer."; @@ -137,27 +140,22 @@ public class CCUtils { return folder; } final Ref generatedRoot = new Ref<>(); - DumbService.allowStartingDumbModeInside(DumbModePermission.MAY_START_BACKGROUND, new Runnable() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - try { - generatedRoot.set(baseDir.createChildDirectory(this, GENERATED_FILES_FOLDER)); - VirtualFile contentRootForFile = - ProjectRootManager.getInstance(module.getProject()).getFileIndex().getContentRootForFile(generatedRoot.get()); - if (contentRootForFile == null) { - return; - } - ModuleRootModificationUtil.updateExcludedFolders(module, contentRootForFile, Collections.emptyList(), - Collections.singletonList(generatedRoot.get().getUrl())); - } - catch (IOException e) { - LOG.info("Failed to create folder for generated files", e); - } + try { + generatedRoot.set(baseDir.createChildDirectory(this, GENERATED_FILES_FOLDER)); + VirtualFile contentRootForFile = + ProjectRootManager.getInstance(module.getProject()).getFileIndex().getContentRootForFile(generatedRoot.get()); + if (contentRootForFile == null) { + return; } - }); + ModuleRootModificationUtil.updateExcludedFolders(module, contentRootForFile, Collections.emptyList(), + Collections.singletonList(generatedRoot.get().getUrl())); + } + catch (IOException e) { + LOG.info("Failed to create folder for generated files", e); + } } }); return generatedRoot.get(); @@ -211,63 +209,6 @@ public class CCUtils { return configurator.isTestFile(file); } - public static void createResourceFile(VirtualFile createdFile, Course course, VirtualFile taskVF) { - VirtualFile lessonVF = taskVF.getParent(); - if (lessonVF == null) { - return; - } - - String taskResourcesPath = FileUtil.join(course.getCourseDirectory(), lessonVF.getName(), taskVF.getName()); - File taskResourceFile = new File(taskResourcesPath); - if (!taskResourceFile.exists()) { - if (!taskResourceFile.mkdirs()) { - LOG.info("Failed to create resources for task " + taskResourcesPath); - } - } - try { - File toFile = new File(taskResourceFile, createdFile.getName()); - FileUtil.copy(new File(createdFile.getPath()), toFile); - } - catch (IOException e) { - LOG.info("Failed to copy created task file to resources " + createdFile.getPath()); - } - } - - - public static void updateResources(Project project, Task task, VirtualFile taskDir) { - Course course = StudyTaskManager.getInstance(project).getCourse(); - if (course == null) { - return; - } - VirtualFile lessonVF = taskDir.getParent(); - if (lessonVF == null) { - return; - } - - String taskResourcesPath = FileUtil.join(course.getCourseDirectory(), lessonVF.getName(), taskDir.getName()); - File taskResourceFile = new File(taskResourcesPath); - if (!taskResourceFile.exists()) { - if (!taskResourceFile.mkdirs()) { - LOG.info("Failed to create resources for task " + taskResourcesPath); - } - } - VirtualFile studentDir = LocalFileSystem.getInstance().findFileByIoFile(taskResourceFile); - if (studentDir == null) { - return; - } - for (Map.Entry entry : task.getTaskFiles().entrySet()) { - String name = entry.getKey(); - VirtualFile answerFile = taskDir.findFileByRelativePath(name); - if (answerFile == null) { - continue; - } - ApplicationManager.getApplication().runWriteAction(() -> { - EduUtils.createStudentFile(CCUtils.class, project, answerFile, studentDir, null, - task instanceof TaskWithSubtasks ? ((TaskWithSubtasks)task).getActiveSubtaskIndex() : 0); - }); - } - } - public static void updateActionGroup(AnActionEvent e) { Presentation presentation = e.getPresentation(); Project project = e.getProject(); @@ -315,4 +256,58 @@ public class CCUtils { } }); } + + @Nullable + public static Lesson createAdditionalLesson(Course course, Project project) { + final VirtualFile baseDir = project.getBaseDir(); + EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(course.getLanguageById()); + + final Lesson lesson = new Lesson(); + lesson.setName(EduNames.PYCHARM_ADDITIONAL); + final Task task = new Task(); + task.setLesson(lesson); + task.setName(EduNames.PYCHARM_ADDITIONAL); + task.setIndex(1); + + VfsUtilCore.visitChildrenRecursively(baseDir, new VirtualFileVisitor(VirtualFileVisitor.NO_FOLLOW_SYMLINKS) { + @Override + public boolean visitFile(@NotNull VirtualFile file) { + final String name = file.getName(); + if (name.equals(EduNames.COURSE_META_FILE) || name.equals(EduNames.HINTS) || name.startsWith(".")) return false; + String sanitizedName = FileUtil.sanitizeFileName(course.getName()); + final String archiveName = sanitizedName.startsWith("_") ? EduNames.COURSE : sanitizedName; + if (name.equals(archiveName + ".zip")) return false; + if (GENERATED_FILES_FOLDER.equals(name) || Project.DIRECTORY_STORE_FOLDER.equals(name)) { + return false; + } + if (file.isDirectory()) return true; + + if (StudyUtils.isTaskDescriptionFile(name) || StudyUtils.isTestsFile(project, name)) return true; + + if (name.contains(".iml") || (configurator != null && configurator.excludeFromArchive(file.getPath()))) { + return false; + } + final TaskFile taskFile = StudyUtils.getTaskFile(project, file); + if (taskFile == null) { + final String path = VfsUtilCore.getRelativePath(file, baseDir); + try { + if (EduUtils.isImage(file.getName())) { + task.addTestsTexts(path, Base64.encodeBase64URLSafeString(FileUtil.loadBytes(file.getInputStream()))); + } + else { + task.addTestsTexts(path, FileUtil.loadTextAndClose(file.getInputStream())); + } + } + catch (IOException e) { + LOG.error("Can't find file " + path); + } + } + return true; + } + }); + if (task.getTestsText().isEmpty()) return null; + lesson.addTask(task); + lesson.setIndex(course.getLessons().size()); + return lesson; + } } diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java index c29d610208dd..cfc2e0dd636b 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java @@ -4,21 +4,21 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectUtil; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; +import com.intellij.openapi.vfs.VirtualFileListener; import com.jetbrains.edu.learning.EduPluginConfigurator; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.courseFormat.tasks.Task; import org.jetbrains.annotations.NotNull; import java.io.File; -public class CCVirtualFileListener extends VirtualFileAdapter { +public class CCVirtualFileListener implements VirtualFileListener { @Override public void fileCreated(@NotNull VirtualFileEvent event) { @@ -48,7 +48,7 @@ public class CCVirtualFileListener extends VirtualFileAdapter { String taskRelativePath = StudyUtils.pathRelativeToTask(createdFile); EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(course.getLanguageById()); - if (configurator != null && configurator.excludeFromArchive(new File(createdFile.getPath()))) { + if (configurator != null && configurator.excludeFromArchive(createdFile.getPath())) { return; } @@ -68,9 +68,7 @@ public class CCVirtualFileListener extends VirtualFileAdapter { return; } - CCUtils.createResourceFile(createdFile, course, taskVF); - - task.addTaskFile(taskRelativePath, 1); + task.addTaskFile(taskRelativePath, task.getTaskFiles().size()+1); } @Override @@ -89,7 +87,7 @@ public class CCVirtualFileListener extends VirtualFileAdapter { return; } Course course = StudyTaskManager.getInstance(project).getCourse(); - if (course == null || path.contains(FileUtil.toSystemIndependentName(course.getCourseDirectory()))) { + if (course == null) { return; } final TaskFile taskFile = StudyUtils.getTaskFile(project, removedFile); @@ -112,7 +110,7 @@ public class CCVirtualFileListener extends VirtualFileAdapter { } VirtualFile courseDir = project.getBaseDir(); CCUtils.updateHigherElements(courseDir.getChildren(), file -> course.getLesson(file.getName()), removedLesson.getIndex(), EduNames.LESSON, -1); - course.getLessons().remove(removedLesson); + course.removeLesson(removedLesson); } private static void deleteTask(@NotNull final Course course, @NotNull final VirtualFile removedTask) { diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCAddAsTaskFile.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCAddAsTaskFile.java index a1bf760c5a6a..9b9904b0f883 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCAddAsTaskFile.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCAddAsTaskFile.java @@ -9,8 +9,8 @@ import com.jetbrains.edu.coursecreator.CCUtils; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.courseFormat.tasks.Task; public class CCAddAsTaskFile extends CCTaskFileActionBase { public static final String ACTION_NAME = "Make Visible to Student"; @@ -21,7 +21,7 @@ public class CCAddAsTaskFile extends CCTaskFileActionBase { protected void performAction(VirtualFile file, Task task, Course course, Project project) { - EduUtils.runUndoableAction(project, ACTION_NAME, new AddTaskFile(file, null, course, project, task)); + EduUtils.runUndoableAction(project, ACTION_NAME, new AddTaskFile(file, null, project, task)); } protected boolean isAvailable(Project project, VirtualFile file) { @@ -31,21 +31,20 @@ public class CCAddAsTaskFile extends CCTaskFileActionBase { private static class AddTaskFile extends BasicUndoableAction { private final VirtualFile myFile; private TaskFile myTaskFile; - private final Course myCourse; private final Project myProject; private final Task myTask; - public AddTaskFile(VirtualFile file, TaskFile taskFile, Course course, Project project, Task task) { + public AddTaskFile(VirtualFile file, TaskFile taskFile, Project project, Task task) { super(file); myFile = file; myTaskFile = taskFile; - myCourse = course; myProject = project; myTask = task; } @Override public void undo() throws UnexpectedUndoException { + if (myTaskFile == null) return; CCHideFromStudent.hideFromStudent(myFile, myProject, myTask.getTaskFiles(), myTaskFile); ProjectView.getInstance(myProject).refresh(); } @@ -59,7 +58,6 @@ public class CCAddAsTaskFile extends CCTaskFileActionBase { myTask.addTaskFile(taskRelativePath, myTask.getTaskFiles().size()); myTaskFile = myTask.getTaskFile(taskRelativePath); } - CCUtils.createResourceFile(myFile, myCourse, StudyUtils.getTaskDir(myFile)); ProjectView.getInstance(myProject).refresh(); } diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java index a199f2d0ebc1..c959e7e99286 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java @@ -14,30 +14,31 @@ import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; -import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.hash.HashMap; import com.intellij.util.io.ZipUtil; import com.jetbrains.edu.coursecreator.CCUtils; import com.jetbrains.edu.coursecreator.ui.CreateCourseArchiveDialog; import com.jetbrains.edu.learning.StudySerializationUtils; -import com.jetbrains.edu.learning.EduPluginConfigurator; import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; +import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.learning.courseFormat.tasks.Task; -import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks; import com.jetbrains.edu.learning.statistics.EduUsagesCollector; import org.jetbrains.annotations.NotNull; import java.io.*; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import java.util.zip.ZipOutputStream; public class CCCreateCourseArchive extends DumbAwareAction { @@ -90,34 +91,27 @@ public class CCCreateCourseArchive extends DumbAwareAction { return; } - EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(course.getLanguageById()); - if (configurator == null) { - return; - } - FileFilter filter = pathname -> !configurator.excludeFromArchive(pathname); - - for (VirtualFile child : baseDir.getChildren()) { - String name = child.getName(); - File fromFile = new File(child.getPath()); - if (CCUtils.GENERATED_FILES_FOLDER.equals(name) || Project.DIRECTORY_STORE_FOLDER.equals(name) - || name.contains("iml") || configurator.excludeFromArchive(fromFile)) { - continue; - } - copyChild(archiveFolder, filter, child, fromFile); - } - ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { archiveFolder.refresh(false, true); Course courseCopy = course.copy(); replaceAnswerFilesWithTaskFiles(courseCopy); + courseCopy.sortLessons(); + createAdditionalFiles(courseCopy); generateJson(archiveFolder, courseCopy); VirtualFileManager.getInstance().refreshWithoutFileWatcher(false); packCourse(archiveFolder, locationDir, zipName, showMessage); synchronize(project); } + private void createAdditionalFiles(Course course) { + final Lesson lesson = CCUtils.createAdditionalLesson(course, project); + if (lesson != null) { + course.addLesson(lesson); + } + } + private void replaceAnswerFilesWithTaskFiles(Course courseCopy) { for (Lesson lesson : courseCopy.getLessons()) { String lessonDirName = EduNames.LESSON + String.valueOf(lesson.getIndex()); @@ -126,61 +120,76 @@ public class CCCreateCourseArchive extends DumbAwareAction { for (Task task : lesson.getTaskList()) { final VirtualFile taskDir = task.getTaskDir(project); if (taskDir == null) continue; - String taskDirName = EduNames.TASK + String.valueOf(task.getIndex()); - VirtualFile studentFileDir = VfsUtil.findRelativeFile(archiveFolder, lessonDirName, taskDirName); - if (studentFileDir == null) { - continue; - } - VirtualFile srcDir = studentFileDir.findChild(EduNames.SRC); - if (srcDir != null) { - studentFileDir = srcDir; - } - if (task instanceof TaskWithSubtasks) { - transformSubtaskTestsToTextFiles(studentFileDir); - } - for (String taskFile : task.getTaskFiles().keySet()) { - VirtualFile answerFile = taskDir.findFileByRelativePath(taskFile); - if (answerFile == null) { - continue; - } - EduUtils.createStudentFile(this, project, answerFile, studentFileDir, task, 0); - } + convertToStudentTaskFiles(task, taskDir); + addTestsToTask(task); + addDescriptions(task); } } } - private void transformSubtaskTestsToTextFiles(VirtualFile studentFileDir) { - Condition isSubtaskTestFile = - file -> CCUtils.isTestsFile(project, file) && file.getName().contains(EduNames.SUBTASK_MARKER); - List subtaskTests = ContainerUtil.filter(Arrays.asList(studentFileDir.getChildren()), isSubtaskTestFile); - for (VirtualFile subtaskTest : subtaskTests) { + private void convertToStudentTaskFiles(Task task, VirtualFile taskDir) { + final HashMap studentTaskFiles = new HashMap<>(); + for (Map.Entry entry : task.getTaskFiles().entrySet()) { + VirtualFile answerFile = taskDir.findFileByRelativePath(entry.getKey()); + if (answerFile == null) { + continue; + } + final TaskFile studentFile = EduUtils.createStudentFile(project, answerFile, task, 0); + if (studentFile != null) { + studentTaskFiles.put(entry.getKey(), studentFile); + } + } + task.taskFiles = studentTaskFiles; + } + + private void addDescriptions(@NotNull final Task task) { + final List descriptions = getDescriptionFiles(task, project); + for (VirtualFile file : descriptions) { try { - subtaskTest.rename(this, subtaskTest.getNameWithoutExtension() + ".txt"); + task.addTaskText(file.getName(), VfsUtilCore.loadText(file)); } catch (IOException e) { - LOG.error(e); + LOG.warn("Failed to load text " + file.getName()); } } } - }); - } - private static void copyChild(VirtualFile archiveFolder, FileFilter filter, VirtualFile child, File fromFile) { - File toFile = new File(archiveFolder.getPath(), child.getName()); - - try { - if (child.isDirectory()) { - FileUtil.copyDir(fromFile, toFile, filter); - } - else { - if (filter.accept(fromFile)) { - FileUtil.copy(fromFile, toFile); + private void addTestsToTask(Task task) { + final List testFiles = getTestFiles(task, project); + for (VirtualFile file : testFiles) { + try { + task.addTestsTexts(file.getName(), VfsUtilCore.loadText(file)); + } + catch (IOException e) { + LOG.warn("Failed to load text " + file.getName()); + } } } - } - catch (IOException e) { - LOG.info("Failed to copy" + fromFile.getPath(), e); - } + + private List getTestFiles(@NotNull Task task, @NotNull Project project) { + List testFiles = new ArrayList<>(); + VirtualFile taskDir = task.getTaskDir(project); + if (taskDir == null) { + return testFiles; + } + testFiles.addAll(Arrays.stream(taskDir.getChildren()) + .filter(file -> StudyUtils.isTestsFile(project, file.getName())) + .collect(Collectors.toList())); + return testFiles; + } + + private List getDescriptionFiles(@NotNull Task task, @NotNull Project project) { + List testFiles = new ArrayList<>(); + VirtualFile taskDir = task.getTaskDir(project); + if (taskDir == null) { + return testFiles; + } + testFiles.addAll(Arrays.stream(taskDir.getChildren()) + .filter(file -> StudyUtils.isTaskDescriptionFile(file.getName())) + .collect(Collectors.toList())); + return testFiles; + } + }); } private static void synchronize(@NotNull final Project project) { @@ -212,7 +221,7 @@ public class CCCreateCourseArchive extends DumbAwareAction { @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") private static void generateJson(VirtualFile parentDir, Course course) { final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation(). - registerTypeAdapter(Task.class, new StudySerializationUtils.Json.TaskSerializer()).create(); + registerTypeAdapter(Task.class, new StudySerializationUtils.Json.TaskAdapter()).create(); final String json = gson.toJson(course); final File courseJson = new File(parentDir.getPath(), EduNames.COURSE_META_FILE); OutputStreamWriter outputStreamWriter = null; diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCCreateLesson.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCCreateLesson.java index a27f895dd305..0436abb5132c 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCCreateLesson.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCCreateLesson.java @@ -83,7 +83,7 @@ public class CCCreateLesson extends CCCreateStudyItemActionBase { @Override protected List getSiblings(@NotNull Course course, @Nullable StudyItem parentItem) { - return course.getLessons(); + return course.getLessons(true); } @Override diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCFromCourseArchive.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCFromCourseArchive.java index 6949ff558c5f..381b6b99101e 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCFromCourseArchive.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCFromCourseArchive.java @@ -1,16 +1,12 @@ package com.jetbrains.edu.coursecreator.actions; -import com.google.gson.FieldNamingPolicy; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonSyntaxException; import com.intellij.icons.AllIcons; import com.intellij.ide.projectView.ProjectView; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; @@ -21,25 +17,23 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; -import com.intellij.platform.templates.github.ZipUtil; import com.jetbrains.edu.coursecreator.CCUtils; -import com.jetbrains.edu.learning.StudySerializationUtils; import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduDocumentListener; import com.jetbrains.edu.learning.core.EduNames; -import com.jetbrains.edu.learning.courseFormat.*; +import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.Lesson; +import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.learning.courseFormat.tasks.Task; +import com.jetbrains.edu.learning.courseGeneration.StudyGenerator; +import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; import org.jetbrains.annotations.NotNull; -import java.io.*; -import java.util.ArrayList; import java.util.Map; -import static com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator.OUR_COURSES_DIR; - public class CCFromCourseArchive extends DumbAwareAction { - private static final Logger LOG = Logger.getInstance(CCFromCourseArchive.class.getName()); - public CCFromCourseArchive() { super("Unpack Course Archive", "Unpack Course Archive", AllIcons.FileTypes.Archive); } @@ -54,7 +48,8 @@ public class CCFromCourseArchive extends DumbAwareAction { } private static void unpackCourseArchive(final Project project) { - FileChooserDescriptor descriptor = new FileChooserDescriptor(true, true, true, true, true, false); + FileChooserDescriptor descriptor = + new FileChooserDescriptor(true, true, true, true, true, false); final VirtualFile virtualFile = FileChooser.chooseFile(descriptor, project, null); if (virtualFile == null) { @@ -62,62 +57,51 @@ public class CCFromCourseArchive extends DumbAwareAction { } final String basePath = project.getBasePath(); if (basePath == null) return; - Reader reader = null; - try { - ZipUtil.unzip(null, new File(basePath), new File(virtualFile.getPath()), null, null, true); - File courseMetaFile = new File(basePath, EduNames.COURSE_META_FILE); - reader = new InputStreamReader(new FileInputStream(courseMetaFile)); - Gson gson = new GsonBuilder() - .registerTypeAdapter(Course.class, new StudySerializationUtils.Json.CourseTypeAdapter(courseMetaFile)) - .registerTypeAdapter(Task.class, new StudySerializationUtils.Json.TaskDeserializer()) - .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) - .create(); - Course course = gson.fromJson(reader, Course.class); - if (course == null) { - Messages.showErrorDialog("This course is incompatible with current version", "Failed to Unpack Course"); - return; - } + Course course = StudyProjectGenerator.getCourse(virtualFile.getPath()); + if (course == null) { + Messages.showErrorDialog("This course is incompatible with current version", "Failed to Unpack Course"); + return; + } + generateFromStudentCourse(project, course); + } - StudyTaskManager.getInstance(project).setCourse(course); - File courseDir = new File(OUR_COURSES_DIR, course.getName() + "-" + project.getName()); - course.setCourseDirectory(courseDir.getPath()); - course.setCourseMode(CCUtils.COURSE_MODE); - project.getBaseDir().refresh(false, true); - int index = 1; - int taskIndex = 1; - for (Lesson lesson : course.getLessons()) { - final VirtualFile lessonDir = project.getBaseDir().findChild(EduNames.LESSON + String.valueOf(index)); - lesson.setIndex(index); - if (lessonDir == null) continue; - for (Task task : lesson.getTaskList()) { - final VirtualFile taskDir = lessonDir.findChild(EduNames.TASK + String.valueOf(taskIndex)); - task.setIndex(taskIndex); - task.setLesson(lesson); - if (taskDir == null) continue; - for (final Map.Entry entry : task.getTaskFiles().entrySet()) { - ApplicationManager.getApplication().runWriteAction(() -> createAnswerFile(project, taskDir, entry)); - } - taskIndex += 1; - } - index += 1; - taskIndex = 1; + public static void generateFromStudentCourse(Project project, Course course) { + StudyTaskManager.getInstance(project).setCourse(course); + course.setCourseMode(CCUtils.COURSE_MODE); + final VirtualFile baseDir = project.getBaseDir(); + final Application application = ApplicationManager.getApplication(); + + application.invokeAndWait(() -> application.runWriteAction(() -> { + final VirtualFile[] children = baseDir.getChildren(); + for (VirtualFile child : children) { + StudyUtils.deleteFile(child); } - course.initCourse(true); - } - catch (JsonSyntaxException | IOException e) { - LOG.error(e.getMessage()); - } - finally { - if (reader != null) { - try { - reader.close(); - } - catch (IOException e) { - LOG.error(e.getMessage()); + StudyGenerator.createCourse(course, baseDir); + })); + baseDir.refresh(false, true); + + int index = 1; + int taskIndex = 1; + for (Lesson lesson : course.getLessons()) { + final VirtualFile lessonDir = project.getBaseDir().findChild(EduNames.LESSON + String.valueOf(index)); + lesson.setIndex(index); + if (lessonDir == null) continue; + for (Task task : lesson.getTaskList()) { + final VirtualFile taskDir = lessonDir.findChild(EduNames.TASK + String.valueOf(taskIndex)); + task.setIndex(taskIndex); + task.setLesson(lesson); + if (taskDir == null) continue; + for (final Map.Entry entry : task.getTaskFiles().entrySet()) { + application.invokeAndWait(() -> application.runWriteAction(() -> createAnswerFile(project, taskDir, entry))); } + taskIndex += 1; } + index += 1; + taskIndex = 1; } + course.initCourse(true); + application.invokeAndWait(() -> StudyUtils.registerStudyToolWindow(course, project)); synchronize(project); } @@ -128,57 +112,44 @@ public class CCFromCourseArchive extends DumbAwareAction { final TaskFile taskFile = taskFileEntry.getValue(); VirtualFile file = userFileDir.findFileByRelativePath(name); assert file != null; - final Document originDocument = FileDocumentManager.getInstance().getDocument(file); - if (originDocument == null) { - return; - } final Document document = FileDocumentManager.getInstance().getDocument(file); if (document == null) return; - CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> document.replaceString(0, document.getTextLength(), originDocument.getCharsSequence())), "Create answer document", "Create answer document"); + CommandProcessor.getInstance().executeCommand(project,() -> ApplicationManager.getApplication().runWriteAction( + () -> document.replaceString(0, document.getTextLength(), document.getCharsSequence())), + "Create answer document", "Create answer document"); EduDocumentListener listener = new EduDocumentListener(taskFile, false); document.addDocumentListener(listener); taskFile.sortAnswerPlaceholders(); - for (int i = taskFile.getActivePlaceholders().size() - 1; i >= 0; i--) { - final AnswerPlaceholder answerPlaceholder = taskFile.getAnswerPlaceholders().get(i); - replaceAnswerPlaceholder(project, document, answerPlaceholder); + + for (AnswerPlaceholder placeholder : taskFile.getActivePlaceholders()) { + replaceAnswerPlaceholder(document, placeholder); } - CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> FileDocumentManager.getInstance().saveDocument(document)), "x", "qwe"); + for (AnswerPlaceholder placeholder : taskFile.getAnswerPlaceholders()) { + placeholder.setUseLength(false); + } + + CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction( + () -> FileDocumentManager.getInstance().saveDocument(document)),"Create answer document", "Create answer document"); document.removeDocumentListener(listener); } - private static void replaceAnswerPlaceholder(@NotNull final Project project, - @NotNull final Document document, - @NotNull final AnswerPlaceholder answerPlaceholder) { - final int offset = answerPlaceholder.getOffset(); - CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> { - final String text = document.getText(TextRange.create(offset, offset + answerPlaceholder.getRealLength())); - answerPlaceholder.setTaskText(text); - answerPlaceholder.init(); - final VirtualFile hints = project.getBaseDir().findChild(EduNames.HINTS); - if (hints != null) { - final ArrayList result = new ArrayList<>(); - for (String hint : answerPlaceholder.getHints()) { - final VirtualFile virtualFile = hints.findChild(hint); - if (virtualFile != null) { - final Document hintDocument = FileDocumentManager.getInstance().getDocument(virtualFile); - if (hintDocument != null) { - final String hintText = hintDocument.getText(); - result.add(hintText); - } - } - } - answerPlaceholder.setHints(result); - } - document.replaceString(offset, offset + answerPlaceholder.getRealLength(), answerPlaceholder.getPossibleAnswer()); - answerPlaceholder.setUseLength(false); + private static void replaceAnswerPlaceholder(@NotNull final Document document, + @NotNull final AnswerPlaceholder placeholder) { + final int offset = placeholder.getOffset(); + final String text = document.getText(TextRange.create(offset, offset + placeholder.getRealLength())); + placeholder.setTaskText(text); + placeholder.init(); + String replacementText = placeholder.getPossibleAnswer(); + + CommandProcessor.getInstance().runUndoTransparentAction(() -> ApplicationManager.getApplication().runWriteAction(() -> { + document.replaceString(offset, offset + placeholder.getRealLength(), replacementText); FileDocumentManager.getInstance().saveDocument(document); - }), "Replace answer placeholder", "From Course Archive"); + })); } private static void synchronize(@NotNull final Project project) { VirtualFileManager.getInstance().refreshWithoutFileWatcher(true); ProjectView.getInstance(project).refresh(); } - } \ No newline at end of file diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java deleted file mode 100644 index 5c16882a183a..000000000000 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.jetbrains.edu.coursecreator.actions; - -import com.intellij.ide.IdeView; -import com.intellij.ide.projectView.ProjectView; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.LangDataKeys; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.project.DumbAwareAction; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileManager; -import com.jetbrains.edu.coursecreator.CCUtils; -import com.jetbrains.edu.learning.StudyTaskManager; -import com.jetbrains.edu.learning.StudyUtils; -import com.jetbrains.edu.learning.core.EduNames; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.learning.courseFormat.tasks.Task; -import com.jetbrains.edu.learning.courseFormat.TaskFile; -import com.jetbrains.edu.learning.courseGeneration.StudyGenerator; -import com.jetbrains.edu.coursecreator.stepik.CCStepicConnector; -import com.jetbrains.edu.learning.courseFormat.CourseInfo; -import com.jetbrains.edu.learning.stepic.EduStepicConnector; -import org.jetbrains.annotations.NotNull; - -import java.io.File; -import java.util.Map; - -import static com.jetbrains.edu.coursecreator.actions.CCFromCourseArchive.createAnswerFile; -import static com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator.OUR_COURSES_DIR; -import static com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator.flushCourse; - -public class CCGetCourseFromStepic extends DumbAwareAction { - - public CCGetCourseFromStepic() { - super("Get Course From Stepik", "Get Course From Stepik", null); - } - - @Override - public void actionPerformed(@NotNull AnActionEvent e) { - final IdeView view = e.getData(LangDataKeys.IDE_VIEW); - final Project project = e.getData(CommonDataKeys.PROJECT); - if (view == null || project == null) { - return; - } - final String courseId = Messages.showInputDialog("Please, enter course id", "Get Course From Stepik", null); - if (StringUtil.isNotEmpty(courseId)) { - ProgressManager.getInstance().run(new com.intellij.openapi.progress.Task.Modal(project, "Creating Course", true) { - @Override - public void run(@NotNull final ProgressIndicator indicator) { - createCourse(project, courseId); - } - }); - } - } - - private static void createCourse(Project project, String courseId) { - final VirtualFile baseDir = project.getBaseDir(); - final CourseInfo info = CCStepicConnector.getCourseInfo(courseId); - if (info == null) return; - - final Course course = EduStepicConnector.getCourse(project, info); - if (course != null) { - flushCourse(course); - - final File courseDirectory = StudyUtils.getCourseDirectory(course); - ApplicationManager.getApplication().invokeAndWait(() -> ApplicationManager.getApplication().runWriteAction(() -> { - final VirtualFile[] children = baseDir.getChildren(); - for (VirtualFile child : children) { - StudyUtils.deleteFile(child); - } - StudyGenerator.createCourse(course, baseDir, courseDirectory, project); - })); - - - StudyTaskManager.getInstance(project).setCourse(course); - File courseDir = new File(OUR_COURSES_DIR, course.getName() + "-" + project.getName()); - course.setCourseDirectory(courseDir.getPath()); - course.setCourseMode(CCUtils.COURSE_MODE); - project.getBaseDir().refresh(false, true); - int index = 1; - int taskIndex = 1; - for (Lesson lesson : course.getLessons()) { - final VirtualFile lessonDir = project.getBaseDir().findChild(EduNames.LESSON + String.valueOf(index)); - lesson.setIndex(index); - if (lessonDir == null) continue; - for (Task task : lesson.getTaskList()) { - final VirtualFile taskDir = lessonDir.findChild(EduNames.TASK + String.valueOf(taskIndex)); - task.setIndex(taskIndex); - task.setLesson(lesson); - if (taskDir == null) continue; - for (final Map.Entry entry : task.getTaskFiles().entrySet()) { - ApplicationManager.getApplication() - .invokeAndWait(() -> ApplicationManager.getApplication().runWriteAction(() -> createAnswerFile(project, taskDir, entry))); - } - taskIndex += 1; - } - index += 1; - taskIndex = 1; - } - course.initCourse(true); - ApplicationManager.getApplication() - .invokeAndWait(() -> StudyUtils.registerStudyToolWindow(course, project)); - } - VirtualFileManager.getInstance().refreshWithoutFileWatcher(true); - ProjectView.getInstance(project).refresh(); - } -} \ No newline at end of file diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCHideFromStudent.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCHideFromStudent.java index 7e13155c0425..accb05906ebf 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCHideFromStudent.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCHideFromStudent.java @@ -1,29 +1,24 @@ package com.jetbrains.edu.coursecreator.actions; import com.intellij.ide.projectView.ProjectView; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.undo.BasicUndoableAction; import com.intellij.openapi.command.undo.UnexpectedUndoException; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.TextEditor; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; -import com.jetbrains.edu.coursecreator.CCUtils; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.courseFormat.tasks.Task; +import org.jetbrains.annotations.NotNull; -import java.io.IOException; import java.util.Map; public class CCHideFromStudent extends CCTaskFileActionBase { - - private static final Logger LOG = Logger.getInstance(CCHideFromStudent.class); private static final String ACTION_NAME = "Hide from Student"; public CCHideFromStudent() { @@ -36,21 +31,19 @@ public class CCHideFromStudent extends CCTaskFileActionBase { if (taskFile == null) { return; } - EduUtils.runUndoableAction(project, ACTION_NAME, new HideTaskFile(project, course, file, task, taskFile)); + EduUtils.runUndoableAction(project, ACTION_NAME, new HideTaskFile(project, file, task, taskFile)); } private static class HideTaskFile extends BasicUndoableAction { private final Project myProject; - private final Course myCourse; private final VirtualFile myFile; private final Task myTask; private final TaskFile myTaskFile; - public HideTaskFile(Project project, Course course, VirtualFile file, Task task, TaskFile taskFile) { + public HideTaskFile(Project project, VirtualFile file, Task task, TaskFile taskFile) { super(file); myProject = project; - myCourse = course; myFile = file; myTask = task; myTaskFile = taskFile; @@ -59,7 +52,6 @@ public class CCHideFromStudent extends CCTaskFileActionBase { @Override public void undo() throws UnexpectedUndoException { myTask.getTaskFiles().put(StudyUtils.pathRelativeToTask(myFile), myTaskFile); - CCUtils.createResourceFile(myFile, myCourse, StudyUtils.getTaskDir(myFile)); if (!myTaskFile.getAnswerPlaceholders().isEmpty() && FileEditorManager.getInstance(myProject).isFileOpen(myFile)) { for (FileEditor fileEditor : FileEditorManager.getInstance(myProject).getEditors(myFile)) { if (fileEditor instanceof TextEditor) { @@ -83,7 +75,7 @@ public class CCHideFromStudent extends CCTaskFileActionBase { } } - public static void hideFromStudent(VirtualFile file, Project project, Map taskFiles, TaskFile taskFile) { + public static void hideFromStudent(VirtualFile file, Project project, Map taskFiles, @NotNull final TaskFile taskFile) { if (!taskFile.getAnswerPlaceholders().isEmpty() && FileEditorManager.getInstance(project).isFileOpen(file)) { for (FileEditor fileEditor : FileEditorManager.getInstance(project).getEditors(file)) { if (fileEditor instanceof TextEditor) { @@ -93,17 +85,6 @@ public class CCHideFromStudent extends CCTaskFileActionBase { } } String taskRelativePath = StudyUtils.pathRelativeToTask(file); - VirtualFile patternFile = StudyUtils.getPatternFile(taskFile, taskRelativePath); - ApplicationManager.getApplication().runWriteAction(() -> { - if (patternFile != null) { - try { - patternFile.delete(CCHideFromStudent.class); - } - catch (IOException e) { - LOG.info(e); - } - } - }); taskFiles.remove(taskRelativePath); } diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java index 896aa1e5d2fb..067a0b47d443 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java @@ -31,10 +31,10 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.FrameWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFile; +import com.intellij.testFramework.LightVirtualFile; import com.intellij.ui.JBColor; import com.jetbrains.edu.coursecreator.CCUtils; import com.jetbrains.edu.learning.StudyTaskManager; @@ -109,33 +109,24 @@ public class CCShowPreview extends DumbAwareAction { return; } - if (taskFile.getActivePlaceholders().isEmpty()) { Messages.showInfoMessage("Preview is available for task files with answer placeholders only", "No Preview for This File"); return; } - VirtualFile generatedFilesFolder = CCUtils.getGeneratedFilesFolder(project, module); - - if (generatedFilesFolder == null) { - return; - } final Task task = taskFile.getTask(); - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - Pair pair = - EduUtils.createStudentFile(this, project, virtualFile, generatedFilesFolder, null, - task instanceof TaskWithSubtasks ? ((TaskWithSubtasks)task).getActiveSubtaskIndex() : 0); - if (pair != null) { - showPreviewDialog(project, pair.getFirst(), pair.getSecond()); - } + ApplicationManager.getApplication().runWriteAction(() -> { + TaskFile studentTaskFile = EduUtils.createStudentFile(project, virtualFile, task.copy(), + task instanceof TaskWithSubtasks ? ((TaskWithSubtasks)task).getActiveSubtaskIndex() : 0); + if (studentTaskFile != null) { + showPreviewDialog(project, studentTaskFile); } }); } - private static void showPreviewDialog(@NotNull Project project, @NotNull VirtualFile userFile, @NotNull TaskFile taskFile) { + private static void showPreviewDialog(@NotNull Project project, @NotNull TaskFile taskFile) { final FrameWrapper showPreviewFrame = new FrameWrapper(project); + final LightVirtualFile userFile = new LightVirtualFile(taskFile.name, taskFile.text); showPreviewFrame.setTitle(userFile.getName()); LabeledEditor labeledEditor = new LabeledEditor(null); final EditorFactory factory = EditorFactory.getInstance(); diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/stepik/CCGetCourseFromStepic.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/stepik/CCGetCourseFromStepic.java new file mode 100644 index 000000000000..7d6cde5ec4cd --- /dev/null +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/stepik/CCGetCourseFromStepic.java @@ -0,0 +1,54 @@ +package com.jetbrains.edu.coursecreator.actions.stepik; + +import com.intellij.ide.IdeView; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.text.StringUtil; +import com.jetbrains.edu.coursecreator.actions.CCFromCourseArchive; +import com.jetbrains.edu.coursecreator.stepik.CCStepicConnector; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.RemoteCourse; +import com.jetbrains.edu.learning.stepic.EduStepicConnector; +import org.jetbrains.annotations.NotNull; + +public class CCGetCourseFromStepic extends DumbAwareAction { + + public CCGetCourseFromStepic() { + super("Get Course From Stepik", "Get Course From Stepik", null); + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + final IdeView view = e.getData(LangDataKeys.IDE_VIEW); + final Project project = e.getData(CommonDataKeys.PROJECT); + if (view == null || project == null) { + return; + } + final String courseId = Messages.showInputDialog("Please, enter course id", "Get Course From Stepik", null); + if (StringUtil.isNotEmpty(courseId)) { + ProgressManager.getInstance().run(new Task.Modal(project, "Creating Course", true) { + @Override + public void run(@NotNull final ProgressIndicator indicator) { + createCourse(project, courseId); + } + }); + } + } + + private static void createCourse(Project project, String courseId) { + final RemoteCourse info = CCStepicConnector.getCourseInfo(courseId); + if (info == null) return; + + final Course course = EduStepicConnector.getCourse(project, info); + if (course == null) return; + + CCFromCourseArchive.generateFromStudentCourse(project, course); + } +} \ No newline at end of file diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/stepik/CCPushCourse.java similarity index 82% rename from python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java rename to python/educational-core/src/com/jetbrains/edu/coursecreator/actions/stepik/CCPushCourse.java index 2ac39ca62623..67b3ecce06d3 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/stepik/CCPushCourse.java @@ -1,4 +1,4 @@ -package com.jetbrains.edu.coursecreator.actions; +package com.jetbrains.edu.coursecreator.actions.stepik; import com.intellij.ide.IdeView; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -11,12 +11,12 @@ import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.coursecreator.stepik.CCStepicConnector; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; +import com.jetbrains.edu.learning.courseFormat.RemoteCourse; import com.jetbrains.edu.learning.statistics.EduUsagesCollector; -import com.jetbrains.edu.coursecreator.stepik.CCStepicConnector; -import com.jetbrains.edu.learning.courseFormat.CourseInfo; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -33,11 +33,8 @@ public class CCPushCourse extends DumbAwareAction { presentation.setEnabledAndVisible(project != null && CCUtils.isCourseCreator(project)); if (project != null) { final Course course = StudyTaskManager.getInstance(project).getCourse(); - if (course != null) { - final int id = course.getId(); - if (id > 0) { - presentation.setText("Update Course on Stepik"); - } + if (course instanceof RemoteCourse) { + presentation.setText("Update Course on Stepik"); } } } @@ -53,19 +50,18 @@ public class CCPushCourse extends DumbAwareAction { if (course == null) { return; } - if (course.getId() > 0) { + if (course instanceof RemoteCourse) { ProgressManager.getInstance().run(new Task.Modal(project, "Updating Course", true) { @Override public void run(@NotNull ProgressIndicator indicator) { for (Lesson lesson : course.getLessons()) { if (lesson.getId() > 0) { - CCStepicConnector.updateLesson(project, lesson, indicator); + CCStepicConnector.updateLesson(project, lesson); } else { - final CourseInfo info = CourseInfo.fromCourse(course); - final int lessonId = CCStepicConnector.postLesson(project, lesson, indicator); + final int lessonId = CCStepicConnector.postLesson(project, lesson); if (lessonId != -1) { - final List sections = info.getSections(); + final List sections = ((RemoteCourse)course).getSections(); final Integer sectionId = sections.get(sections.size() - 1); CCStepicConnector.postUnit(lessonId, lesson.getIndex(), sectionId); } diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/stepik/CCPushLesson.java similarity index 87% rename from python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java rename to python/educational-core/src/com/jetbrains/edu/coursecreator/actions/stepik/CCPushLesson.java index 3eda00f494fa..b56cbedd251f 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/stepik/CCPushLesson.java @@ -1,4 +1,4 @@ -package com.jetbrains.edu.coursecreator.actions; +package com.jetbrains.edu.coursecreator.actions.stepik; import com.intellij.ide.IdeView; import com.intellij.ide.util.DirectoryChooserUtil; @@ -12,11 +12,11 @@ import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDirectory; import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.coursecreator.stepik.CCStepicConnector; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.coursecreator.stepik.CCStepicConnector; -import com.jetbrains.edu.learning.courseFormat.CourseInfo; +import com.jetbrains.edu.learning.courseFormat.RemoteCourse; import com.jetbrains.edu.learning.stepic.EduStepicNames; import org.jetbrains.annotations.NotNull; @@ -36,7 +36,7 @@ public class CCPushLesson extends DumbAwareAction { return; } final Course course = StudyTaskManager.getInstance(project).getCourse(); - if (course == null) { + if (course == null || !(course instanceof RemoteCourse)) { return; } if (!course.getCourseMode().equals(CCUtils.COURSE_MODE)) return; @@ -45,7 +45,7 @@ public class CCPushLesson extends DumbAwareAction { return; } final Lesson lesson = course.getLesson(lessonDir.getName()); - if (lesson != null && course.getId() > 0) { + if (lesson != null && ((RemoteCourse)course).getId() > 0) { e.getPresentation().setEnabledAndVisible(true); if (lesson.getId() <= 0) { e.getPresentation().setText("Upload Lesson to Stepik"); @@ -61,7 +61,7 @@ public class CCPushLesson extends DumbAwareAction { return; } final Course course = StudyTaskManager.getInstance(project).getCourse(); - if (course == null) { + if (course == null || !(course instanceof RemoteCourse)) { return; } PsiDirectory lessonDir = DirectoryChooserUtil.getOrChooseDirectory(view); @@ -77,12 +77,11 @@ public class CCPushLesson extends DumbAwareAction { public void run(@NotNull ProgressIndicator indicator) { indicator.setText("Uploading lesson to " + EduStepicNames.STEPIC_URL); if (lesson.getId() > 0) { - CCStepicConnector.updateLesson(project, lesson, indicator); + CCStepicConnector.updateLesson(project, lesson); } else { - final CourseInfo info = CourseInfo.fromCourse(course); - final int lessonId = CCStepicConnector.postLesson(project, lesson, indicator); - final List sections = info.getSections(); + final int lessonId = CCStepicConnector.postLesson(project, lesson); + final List sections = ((RemoteCourse)course).getSections(); final Integer sectionId = sections.get(sections.size()-1); CCStepicConnector.postUnit(lessonId, lesson.getIndex(), sectionId); } diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCPushTask.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/stepik/CCPushTask.java similarity index 92% rename from python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCPushTask.java rename to python/educational-core/src/com/jetbrains/edu/coursecreator/actions/stepik/CCPushTask.java index 7346b1286cf6..36ac0b369e4c 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCPushTask.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/stepik/CCPushTask.java @@ -1,4 +1,4 @@ -package com.jetbrains.edu.coursecreator.actions; +package com.jetbrains.edu.coursecreator.actions.stepik; import com.intellij.ide.IdeView; import com.intellij.ide.util.DirectoryChooserUtil; @@ -12,11 +12,12 @@ import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDirectory; import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.coursecreator.stepik.CCStepicConnector; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.coursecreator.stepik.CCStepicConnector; +import com.jetbrains.edu.learning.courseFormat.RemoteCourse; import com.jetbrains.edu.learning.stepic.EduStepicNames; import org.jetbrains.annotations.NotNull; @@ -34,7 +35,7 @@ public class CCPushTask extends DumbAwareAction { return; } final Course course = StudyTaskManager.getInstance(project).getCourse(); - if (course == null) { + if (course == null || !(course instanceof RemoteCourse)) { return; } if (!course.getCourseMode().equals(CCUtils.COURSE_MODE)) return; @@ -45,7 +46,7 @@ public class CCPushTask extends DumbAwareAction { final PsiDirectory lessonDir = taskDir.getParentDirectory(); if (lessonDir == null) return; final Lesson lesson = course.getLesson(lessonDir.getName()); - if (lesson != null && lesson.getId() > 0 && course.getId() > 0) { + if (lesson != null && lesson.getId() > 0 && ((RemoteCourse)course).getId() > 0) { e.getPresentation().setEnabledAndVisible(true); final com.jetbrains.edu.learning.courseFormat.tasks.Task task = lesson.getTask(taskDir.getName()); if (task.getStepId() <= 0) { diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/handlers/CCLessonMoveHandlerDelegate.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/handlers/CCLessonMoveHandlerDelegate.java index 15da6057e0c7..3b163cd947ca 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/handlers/CCLessonMoveHandlerDelegate.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/handlers/CCLessonMoveHandlerDelegate.java @@ -109,7 +109,7 @@ public class CCLessonMoveHandlerDelegate extends MoveHandlerDelegate { newItemIndex - 1, EduNames.LESSON, 1); sourceLesson.setIndex(newItemIndex); - Collections.sort(course.getLessons(), EduUtils.INDEX_COMPARATOR); + Collections.sort(course.getLessons(true), EduUtils.INDEX_COMPARATOR); ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/intellij/EduCCModuleBuilder.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/intellij/EduCCModuleBuilder.java index 927cb26a4548..b8fc3e83c486 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/intellij/EduCCModuleBuilder.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/intellij/EduCCModuleBuilder.java @@ -28,7 +28,6 @@ import com.jetbrains.edu.coursecreator.ui.CCNewProjectPanel; import com.jetbrains.edu.learning.EduPluginConfigurator; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; import com.jetbrains.edu.learning.intellij.generation.EduCourseModuleBuilder; import org.jdom.JDOMException; import org.jetbrains.annotations.NotNull; @@ -36,7 +35,6 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.text.JTextComponent; -import java.io.File; import java.io.IOException; import java.util.Collections; import java.util.function.Function; @@ -93,8 +91,6 @@ class EduCCModuleBuilder extends EduCourseModuleBuilder { Language language = wrapper.getLanguage(); course.setLanguage(language.getID()); course.setCourseMode(CCUtils.COURSE_MODE); - File courseDir = new File(StudyProjectGenerator.OUR_COURSES_DIR, myPanel.getName() + "-" + project.getName()); - course.setCourseDirectory(courseDir.getPath()); StudyTaskManager.getInstance(project).setCourse(course); EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(language); String languageName = language.getDisplayName(); diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/settings/CCOptions.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/settings/CCOptions.java index 6f5e3cd7768b..85b018c7df81 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/settings/CCOptions.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/settings/CCOptions.java @@ -19,15 +19,13 @@ public class CCOptions implements StudyOptionsProvider { if (!StudySettings.getInstance().isCourseCreatorEnabled()) return null; if (CCSettings.getInstance().useHtmlAsDefaultTaskFormat()) { myHtmlRadioButton.setSelected(true); - IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> { - IdeFocusManager.getGlobalInstance().requestFocus(myHtmlRadioButton, true); - }); + IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown( + () -> IdeFocusManager.getGlobalInstance().requestFocus(myHtmlRadioButton, true)); } else { myMarkdownRadioButton.setSelected(true); - IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> { - IdeFocusManager.getGlobalInstance().requestFocus(myMarkdownRadioButton, true); - }); + IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown( + () -> IdeFocusManager.getGlobalInstance().requestFocus(myMarkdownRadioButton, true)); } return myPanel; } diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/stepik/CCStepicConnector.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/stepik/CCStepicConnector.java index 1b45fb108668..c5ac73922cb9 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/stepik/CCStepicConnector.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/stepik/CCStepicConnector.java @@ -8,23 +8,19 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileFilter; +import com.jetbrains.edu.coursecreator.CCUtils; import com.jetbrains.edu.learning.StudySerializationUtils; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.core.EduNames; -import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.CourseInfo; import com.jetbrains.edu.learning.courseFormat.Lesson; +import com.jetbrains.edu.learning.courseFormat.RemoteCourse; import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.stepic.EduStepicAuthorizedClient; import com.jetbrains.edu.learning.stepic.EduStepicNames; import com.jetbrains.edu.learning.stepic.StepicUser; import com.jetbrains.edu.learning.stepic.StepicWrappers; -import org.apache.commons.codec.binary.Base64; import org.apache.http.HttpEntity; import org.apache.http.HttpStatus; import org.apache.http.StatusLine; @@ -48,7 +44,7 @@ public class CCStepicConnector { private CCStepicConnector() { } - public static CourseInfo getCourseInfo(String courseId) { + public static RemoteCourse getCourseInfo(String courseId) { final String url = EduStepicNames.COURSES + "/" + courseId; try { final StepicWrappers.CoursesContainer coursesContainer = @@ -65,13 +61,16 @@ public class CCStepicConnector { ProgressManager.getInstance().run(new com.intellij.openapi.progress.Task.Modal(project, "Uploading Course", true) { @Override public void run(@NotNull final ProgressIndicator indicator) { - postCourse(project, course, indicator); + postCourse(project, course); } }); } - private static void postCourse(final Project project, @NotNull Course course, @NotNull final ProgressIndicator indicator) { - indicator.setText("Uploading course to " + EduStepicNames.STEPIC_URL); + private static void postCourse(final Project project, @NotNull Course course) { + final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); + if (indicator != null) { + indicator.setText("Uploading course to " + EduStepicNames.STEPIC_URL); + } final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/courses"); final StepicUser currentUser = EduStepicAuthorizedClient.getCurrentUser(); @@ -105,60 +104,33 @@ public class CCStepicConnector { LOG.error("Failed to push " + responseString); return; } - final CourseInfo postedCourse = new Gson().fromJson(responseString, StepicWrappers.CoursesContainer.class).courses.get(0); - course.setId(postedCourse.getId()); + final RemoteCourse postedCourse = new Gson().fromJson(responseString, StepicWrappers.CoursesContainer.class).courses.get(0); + postedCourse.setLessons(course.getLessons(true)); + postedCourse.setAuthors(course.getAuthors()); + postedCourse.setCourseMode(CCUtils.COURSE_MODE); final int sectionId = postModule(postedCourse.getId(), 1, String.valueOf(postedCourse.getName())); int position = 1; for (Lesson lesson : course.getLessons()) { - indicator.checkCanceled(); - final int lessonId = postLesson(project, lesson, indicator); + if (indicator != null) { + indicator.checkCanceled(); + } + final int lessonId = postLesson(project, lesson); postUnit(lessonId, position, sectionId); position += 1; } - ApplicationManager.getApplication().runReadAction(() -> postAdditionalFiles(project, postedCourse.getId(), indicator)); + ApplicationManager.getApplication().runReadAction(() -> postAdditionalFiles(course, project, postedCourse.getId())); + StudyTaskManager.getInstance(project).setCourse(postedCourse); } catch (IOException e) { LOG.error(e.getMessage()); } } - private static void postAdditionalFiles(@NotNull final Project project, int id, ProgressIndicator indicator) { - final VirtualFile baseDir = project.getBaseDir(); - final List files = VfsUtil.getChildren(baseDir, new VirtualFileFilter() { - @Override - public boolean accept(VirtualFile file) { - final String name = file.getName(); - return !name.contains(EduNames.LESSON) && !name.equals(EduNames.COURSE_META_FILE) && !name.equals(EduNames.HINTS) && - !"pyc".equals(file.getExtension()) && !file.isDirectory() && !name.equals(EduNames.TEST_HELPER) && !name.startsWith("."); - } - }); - - if (!files.isEmpty()) { + private static void postAdditionalFiles(Course course, @NotNull final Project project, int id) { + final Lesson lesson = CCUtils.createAdditionalLesson(course, project); + if (lesson != null) { final int sectionId = postModule(id, 2, EduNames.PYCHARM_ADDITIONAL); - final Lesson lesson = new Lesson(); - lesson.setName(EduNames.PYCHARM_ADDITIONAL); - final Task task = new Task(); - task.setLesson(lesson); - task.setName(EduNames.PYCHARM_ADDITIONAL); - task.setIndex(1); - for (VirtualFile file : files) { - try { - if (file != null) { - if (EduUtils.isImage(file.getName())) { - task.addTestsTexts(file.getName(), Base64.encodeBase64URLSafeString(FileUtil.loadBytes(file.getInputStream()))); - } - else { - task.addTestsTexts(file.getName(), FileUtil.loadTextAndClose(file.getInputStream())); - } - } - } - catch (IOException e) { - LOG.error("Can't find file " + file.getPath()); - } - } - lesson.addTask(task); - lesson.setIndex(1); - final int lessonId = postLesson(project, lesson, indicator); + final int lessonId = postLesson(project, lesson); postUnit(lessonId, 1, sectionId); } } @@ -255,7 +227,7 @@ public class CCStepicConnector { return -1; } - public static int updateLesson(@NotNull final Project project, @NotNull final Lesson lesson, ProgressIndicator indicator) { + public static int updateLesson(@NotNull final Project project, @NotNull final Lesson lesson) { final HttpPut request = new HttpPut(EduStepicNames.STEPIC_API_URL + EduStepicNames.LESSONS + String.valueOf(lesson.getId())); String requestBody = new Gson().toJson(new StepicWrappers.LessonWrapper(lesson)); @@ -273,13 +245,16 @@ public class CCStepicConnector { LOG.error("Failed to push " + responseString); return -1; } - final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0); + final Lesson postedLesson = new Gson().fromJson(responseString, RemoteCourse.class).getLessons().get(0); for (Integer step : postedLesson.steps) { deleteTask(step); } for (Task task : lesson.getTaskList()) { - indicator.checkCanceled(); + final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); + if (indicator != null) { + indicator.checkCanceled(); + } postTask(project, task, lesson.getId()); } return lesson.getId(); @@ -290,7 +265,7 @@ public class CCStepicConnector { return -1; } - public static int postLesson(@NotNull final Project project, @NotNull final Lesson lesson, ProgressIndicator indicator) { + public static int postLesson(@NotNull final Project project, @NotNull final Lesson lesson) { final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/lessons"); String requestBody = new Gson().toJson(new StepicWrappers.LessonWrapper(lesson)); @@ -308,10 +283,13 @@ public class CCStepicConnector { LOG.error("Failed to push " + responseString); return 0; } - final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0); + final Lesson postedLesson = new Gson().fromJson(responseString, RemoteCourse.class).getLessons(true).get(0); lesson.setId(postedLesson.getId()); for (Task task : lesson.getTaskList()) { - indicator.checkCanceled(); + final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); + if (indicator != null) { + indicator.checkCanceled(); + } postTask(project, task, postedLesson.getId()); } return postedLesson.getId(); diff --git a/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java b/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java index da549bc885e8..aaa21bbf5ead 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java @@ -21,7 +21,8 @@ import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.File; +import java.util.Collections; +import java.util.List; public interface EduPluginConfigurator { String EP_NAME = "Edu.pluginConfigurator"; @@ -64,7 +65,7 @@ public interface EduPluginConfigurator { /** * Used in educator plugin to filter files to be packed into course archive */ - boolean excludeFromArchive(@NotNull File pathname); + boolean excludeFromArchive(@NotNull String name); /** * @return true for all the test files including tests for subtasks @@ -128,4 +129,7 @@ public interface EduPluginConfigurator { @Nullable String moduleDir) { } + default List getBundledCoursePaths() { + return Collections.emptyList(); + } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudyProjectComponent.java b/python/educational-core/src/com/jetbrains/edu/learning/StudyProjectComponent.java index 0d26cbadeacf..e2e84ae3ea67 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/StudyProjectComponent.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/StudyProjectComponent.java @@ -25,8 +25,8 @@ import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; +import com.intellij.openapi.vfs.VirtualFileListener; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; @@ -39,9 +39,9 @@ import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.*; import com.jetbrains.edu.learning.courseFormat.tasks.Task; +import com.jetbrains.edu.learning.courseGeneration.StudyGenerator; import com.jetbrains.edu.learning.editor.StudyEditorFactoryListener; import com.jetbrains.edu.learning.statistics.EduUsagesCollector; -import com.jetbrains.edu.learning.courseFormat.CourseInfo; import com.jetbrains.edu.learning.stepic.EduStepicConnector; import com.jetbrains.edu.learning.ui.StudyToolWindow; import com.jetbrains.edu.learning.ui.StudyToolWindowFactory; @@ -50,14 +50,12 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.event.HyperlinkEvent; -import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import static com.jetbrains.edu.learning.StudyUtils.execCancelable; -import static com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator.flushCourse; public class StudyProjectComponent implements ProjectComponent { @@ -80,39 +78,18 @@ public class StudyProjectComponent implements ProjectComponent { } if (course != null && !course.isAdaptive() && !course.isUpToDate()) { - final Notification notification = - new Notification("Update.course", "Course Updates", "Course is ready to update", NotificationType.INFORMATION, - new NotificationListener() { - @Override - public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { - FileEditorManagerEx.getInstanceEx(myProject).closeAllFiles(); - - ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { - ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); - return execCancelable(() -> { - updateCourse(); - return true; - }); - }, "Updating Course", true, myProject); - EduUtils.synchronize(); - course.setUpdated(); - - } - }); - notification.notify(myProject); - + updateAvailable(course); } StudyUtils.registerStudyToolWindow(course, myProject); StartupManager.getInstance(myProject).runWhenProjectIsInitialized(() -> ApplicationManager.getApplication().invokeLater( (DumbAwareRunnable)() -> ApplicationManager.getApplication().runWriteAction((DumbAwareRunnable)() -> { - Course course1 = StudyTaskManager.getInstance(myProject).getCourse(); - if (course1 != null) { + if (course != null) { UISettings instance = UISettings.getInstance(); instance.setHideToolStripes(false); instance.fireUISettingsChanged(); registerShortcuts(); - EduUsagesCollector.projectTypeOpened(course1.isAdaptive() ? EduNames.ADAPTIVE : EduNames.STUDY); + EduUsagesCollector.projectTypeOpened(course.isAdaptive() ? EduNames.ADAPTIVE : EduNames.STUDY); } }))); @@ -128,6 +105,29 @@ public class StudyProjectComponent implements ProjectComponent { }); } + private void updateAvailable(Course course) { + final Notification notification = + new Notification("Update.course", "Course Updates", "Course is ready to update", NotificationType.INFORMATION, + new NotificationListener() { + @Override + public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { + FileEditorManagerEx.getInstanceEx(myProject).closeAllFiles(); + + ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { + ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); + return execCancelable(() -> { + updateCourse(); + return true; + }); + }, "Updating Course", true, myProject); + EduUtils.synchronize(); + course.setUpdated(); + + } + }); + notification.notify(myProject); + } + private void registerShortcuts() { StudyToolWindow window = StudyUtils.getStudyToolWindow(myProject); if (window != null) { @@ -148,18 +148,9 @@ public class StudyProjectComponent implements ProjectComponent { private void updateCourse() { final Course currentCourse = StudyTaskManager.getInstance(myProject).getCourse(); - final CourseInfo info = CourseInfo.fromCourse(currentCourse); - if (info == null) return; - - final File resourceDirectory = new File(currentCourse.getCourseDirectory()); - if (resourceDirectory.exists()) { - FileUtil.delete(resourceDirectory); - } - - final Course course = EduStepicConnector.getCourse(myProject, info); - + if (currentCourse == null || !(currentCourse instanceof RemoteCourse)) return; + final Course course = EduStepicConnector.getCourse(myProject, (RemoteCourse)currentCourse); if (course == null) return; - flushCourse(course); course.initCourse(false); EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(course.getLanguageById()); @@ -171,19 +162,19 @@ public class StudyProjectComponent implements ProjectComponent { final ArrayList updatedLessons = new ArrayList<>(); int lessonIndex = 0; - for (Lesson lesson : course.getLessons()) { + for (Lesson lesson : course.getLessons(true)) { lessonIndex += 1; Lesson studentLesson = currentCourse.getLesson(lesson.getId()); final String lessonDirName = EduNames.LESSON + String.valueOf(lessonIndex); - final File lessonDir = new File(myProject.getBasePath(), lessonDirName); - if (!lessonDir.exists()){ - final File fromLesson = new File(resourceDirectory, lessonDirName); + final VirtualFile baseDir = myProject.getBaseDir(); + final VirtualFile lessonDir = baseDir.findChild(lessonDirName); + if (lessonDir == null) { try { - FileUtil.copyDir(fromLesson, lessonDir); + StudyGenerator.createLesson(lesson, baseDir); } catch (IOException e) { - LOG.warn("Failed to copy lesson " + fromLesson.getPath()); + LOG.error("Failed to create lesson"); } lesson.setIndex(lessonIndex); lesson.initLesson(currentCourse, false); @@ -211,15 +202,14 @@ public class StudyProjectComponent implements ProjectComponent { task.setIndex(index); final String taskDirName = EduNames.TASK + String.valueOf(index); - final File toTask = new File(lessonDir, taskDirName); + final VirtualFile taskDir = lessonDir.findChild(taskDirName); - final String taskPath = FileUtil.join(resourceDirectory.getPath(), lessonDirName, taskDirName); - final File taskDir = new File(taskPath); - if (!taskDir.exists()) return; - final File[] taskFiles = taskDir.listFiles(); - if (taskFiles == null) continue; - for (File fromFile : taskFiles) { - copyFile(fromFile, new File(toTask, fromFile.getName())); + if (taskDir != null) return; + try { + StudyGenerator.createTask(task, lessonDir); + } + catch (IOException e) { + LOG.error("Failed to create task"); } tasks.add(task); } @@ -232,17 +222,6 @@ public class StudyProjectComponent implements ProjectComponent { notification.notify(myProject); } - private static void copyFile(@NotNull final File from, @NotNull final File to) { - if (from.exists()) { - try { - FileUtil.copyFileOrDir(from, to); - } - catch (IOException e) { - LOG.warn("Failed to copy " + from.getName()); - } - } - } - private void addShortcut(@NotNull final String actionIdString, @NotNull final String[] shortcuts) { KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx(); for (Keymap keymap : keymapManager.getAllKeymaps()) { @@ -333,7 +312,7 @@ public class StudyProjectComponent implements ProjectComponent { return module.getComponent(StudyProjectComponent.class); } - private class FileCreatedByUserListener extends VirtualFileAdapter { + private class FileCreatedByUserListener implements VirtualFileListener { @Override public void fileCreated(@NotNull VirtualFileEvent event) { if (myProject.isDisposed()) return; diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudySerializationUtils.java b/python/educational-core/src/com/jetbrains/edu/learning/StudySerializationUtils.java index c782034265d9..043625347769 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/StudySerializationUtils.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/StudySerializationUtils.java @@ -5,17 +5,13 @@ import com.google.gson.reflect.TypeToken; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.EditorFactory; -import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtilRt; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.hash.HashMap; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; -import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.StudyStatus; import com.jetbrains.edu.learning.courseFormat.tasks.*; import com.jetbrains.edu.learning.stepic.EduStepicConnector; @@ -26,7 +22,6 @@ import org.jdom.Element; import org.jdom.output.XMLOutputter; import org.jetbrains.annotations.NotNull; -import java.io.File; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; @@ -62,6 +57,7 @@ public class StudySerializationUtils { public static class Xml { public final static String COURSE_ELEMENT = "courseElement"; public final static String MAIN_ELEMENT = "StudyTaskManager"; + public final static String REMOTE_COURSE = "RemoteCourse"; public static final String MAP = "map"; public static final String KEY = "key"; public static final String VALUE = "value"; @@ -262,14 +258,17 @@ public class StudySerializationUtils { final Element adaptive = getChildWithName(courseElement, ADAPTIVE); for (Element lesson : getChildList(courseElement, LESSONS)) { for (Element task : getChildList(lesson, TASK_LIST)) { - final Element lastSubtaskIndex = getChildWithName(task, LAST_SUBTASK_INDEX); - final Element theoryTask = getChildWithName(task, THEORY_TAG); - final Element adaptiveParams = getChildWithName(task, ADAPTIVE_TASK_PARAMETERS); - final boolean hasAdaptiveParams = !adaptiveParams.getChildren().isEmpty(); - if (Integer.valueOf(lastSubtaskIndex.getAttributeValue(VALUE)) != 0) { + final Element lastSubtaskIndex = getChildWithName(task, LAST_SUBTASK_INDEX, true); //could be broken by 3->4 migration + final Element adaptiveParams = getChildWithName(task, ADAPTIVE_TASK_PARAMETERS, true); + Element theoryTask = getChildWithName(task, THEORY_TAG, true); + if (theoryTask == null && adaptiveParams != null) { + theoryTask = getChildWithName(adaptiveParams, THEORY_TAG, true); + } + final boolean hasAdaptiveParams = adaptiveParams != null && !adaptiveParams.getChildren().isEmpty(); + if (lastSubtaskIndex != null && Integer.valueOf(lastSubtaskIndex.getAttributeValue(VALUE)) != 0) { task.setName(TASK_WITH_SUBTASKS); } - else if (Boolean.valueOf(theoryTask.getAttributeValue(VALUE))) { + else if (theoryTask != null && Boolean.valueOf(theoryTask.getAttributeValue(VALUE))) { task.setName(THEORY_TASK); } else if (hasAdaptiveParams) { @@ -455,61 +454,6 @@ public class StudySerializationUtils { private Json() { } - public static class CourseTypeAdapter implements JsonDeserializer { - - private final File myCourseFile; - - public CourseTypeAdapter(File courseFile) { - myCourseFile = courseFile; - } - - @Override - public Course deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - JsonObject courseObject = json.getAsJsonObject(); - JsonArray lessons = courseObject.getAsJsonArray(LESSONS); - for (int lessonIndex = 1; lessonIndex <= lessons.size(); lessonIndex++) { - JsonObject lessonObject = lessons.get(lessonIndex - 1).getAsJsonObject(); - JsonArray tasks = lessonObject.getAsJsonArray(TASK_LIST); - for (int taskIndex = 1; taskIndex <= tasks.size(); taskIndex++) { - JsonObject taskObject = tasks.get(taskIndex - 1).getAsJsonObject(); - for (Map.Entry taskFile : taskObject.getAsJsonObject(TASK_FILES).entrySet()) { - String name = taskFile.getKey(); - String filePath = FileUtil.join(myCourseFile.getParent(), EduNames.LESSON + lessonIndex, EduNames.TASK + taskIndex, name); - VirtualFile resourceFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(filePath)); - if (resourceFile == null) { - continue; - } - Document document = FileDocumentManager.getInstance().getDocument(resourceFile); - if (document == null) { - continue; - } - JsonObject taskFileObject = taskFile.getValue().getAsJsonObject(); - JsonArray placeholders = taskFileObject.getAsJsonArray(PLACEHOLDERS); - for (JsonElement placeholder : placeholders) { - convertToAbsoluteOffset(document, placeholder); - if (placeholder.getAsJsonObject().getAsJsonObject(SUBTASK_INFOS) == null) { - convertToSubtaskInfo(placeholder.getAsJsonObject()); - removeIndexFromSubtaskInfos(placeholder.getAsJsonObject()); - } - } - } - } - } - return new GsonBuilder().registerTypeAdapter(Task.class, new TaskDeserializer()).create().fromJson(json, Course.class); - } - - private static void convertToAbsoluteOffset(Document document, JsonElement placeholder) { - JsonObject placeholderObject = placeholder.getAsJsonObject(); - if (placeholderObject.getAsJsonPrimitive(OFFSET) != null) { - return; - } - int line = placeholderObject.getAsJsonPrimitive(LINE).getAsInt(); - int start = placeholderObject.getAsJsonPrimitive(START).getAsInt(); - int offset = document.getLineStartOffset(line) + start; - placeholderObject.addProperty(OFFSET, offset); - } - } - public static class StepicStepOptionsAdapter implements JsonDeserializer { @Override public StepicWrappers.StepOptions deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) @@ -537,7 +481,7 @@ public class StudySerializationUtils { return stepOptions; } - private JsonObject convertToThirdVersion(JsonObject stepOptionsJson) { + private static JsonObject convertToThirdVersion(JsonObject stepOptionsJson) { if (!stepOptionsJson.has(LAST_SUBTASK)) return stepOptionsJson; final int lastSubtaskIndex = stepOptionsJson.get(LAST_SUBTASK).getAsInt(); if (lastSubtaskIndex == 0) return stepOptionsJson; @@ -549,7 +493,7 @@ public class StudySerializationUtils { } } final JsonArray descriptions = stepOptionsJson.getAsJsonArray(TEXTS); - if (descriptions.size() > 0) { + if (descriptions != null && descriptions.size() > 0) { final JsonObject fileWrapper = descriptions.get(0).getAsJsonObject(); if (fileWrapper.has(NAME)) { replaceWithSubtask(fileWrapper); @@ -558,7 +502,7 @@ public class StudySerializationUtils { return stepOptionsJson; } - private void replaceWithSubtask(JsonObject fileWrapper) { + private static void replaceWithSubtask(JsonObject fileWrapper) { final String file = fileWrapper.get(NAME).getAsString(); final String extension = FileUtilRt.getExtension(file); final String name = FileUtil.getNameWithoutExtension(file); @@ -673,7 +617,7 @@ public class StudySerializationUtils { subtaskInfo.addProperty(POSSIBLE_ANSWER, placeholderObject.getAsJsonPrimitive(POSSIBLE_ANSWER).getAsString()); } - public static class TaskSerializer implements JsonSerializer { + public static class TaskAdapter implements JsonSerializer, JsonDeserializer { @Override public JsonElement serialize(Task src, Type typeOfSrc, JsonSerializationContext context) { @@ -683,9 +627,6 @@ public class StudySerializationUtils { task.add(TASK_TYPE, new JsonPrimitive(src.getTaskType())); return task; } - } - - public static class TaskDeserializer implements JsonDeserializer { @Override public Task deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { @@ -705,7 +646,7 @@ public class StudySerializationUtils { } } } - LOG.warn("No task type found in json " + json.getAsString()); + LOG.warn("No task type found in json " + json.toString()); return null; } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java b/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java index 9d74279928a7..d64896ca4bb8 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java @@ -1,5 +1,9 @@ package com.jetbrains.edu.learning; +import com.intellij.ide.fileTemplates.FileTemplate; +import com.intellij.ide.fileTemplates.FileTemplateManager; +import com.intellij.ide.fileTemplates.FileTemplateUtil; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.components.State; @@ -7,25 +11,31 @@ import com.intellij.openapi.components.Storage; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiManager; import com.intellij.ui.JBColor; import com.intellij.util.containers.hash.HashMap; import com.intellij.util.xmlb.XmlSerializer; import com.intellij.util.xmlb.annotations.Transient; +import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.*; import com.jetbrains.edu.learning.courseFormat.tasks.Task; -import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; import com.jetbrains.edu.learning.ui.StudyToolWindow; import org.jdom.Element; import org.jdom.output.XMLOutputter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import static com.jetbrains.edu.learning.StudySerializationUtils.Xml.REMOTE_COURSE; + /** * Implementation of class which contains all the information * about study in context of current project @@ -39,7 +49,6 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb public int VERSION = 5; public Map> myUserTests = new HashMap<>(); - public List myInvisibleFiles = new ArrayList<>(); public boolean myShouldUseJavaFx = StudyUtils.hasJavaFx(); private StudyToolWindow.StudyToolWindowMode myToolWindowMode = StudyToolWindow.StudyToolWindowMode.TEXT; @@ -118,10 +127,32 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb if (myCourse == null) { return null; } + + return serialize(); + } + + @NotNull + private Element serialize() { Element el = new Element("taskManager"); - Element courseElement = new Element(StudySerializationUtils.Xml.MAIN_ELEMENT); - XmlSerializer.serializeInto(this, courseElement); - el.addContent(courseElement); + Element taskManagerElement = new Element(StudySerializationUtils.Xml.MAIN_ELEMENT); + XmlSerializer.serializeInto(this, taskManagerElement); + + if (myCourse instanceof RemoteCourse) { + try { + Element course = new Element(REMOTE_COURSE); + //noinspection RedundantCast + XmlSerializer.serializeInto((RemoteCourse)myCourse, course); + + final Element xmlCourse = StudySerializationUtils.Xml.getChildWithName(taskManagerElement, StudySerializationUtils.COURSE); + xmlCourse.removeContent(); + xmlCourse.addContent(course); + } + catch (StudySerializationUtils.StudyUnrecognizedFormatException e) { + LOG.error("Failed to serialize remote course"); + } + } + + el.addContent(taskManagerElement); return el; } @@ -142,20 +173,15 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb state = StudySerializationUtils.Xml.convertToForthVersion(state); case 4: state = StudySerializationUtils.Xml.convertToFifthVersion(state); + updateTestHelper(); //uncomment for future versions //case 5: // state = StudySerializationUtils.Xml.convertToSixthVersion(state, myProject); } - XmlSerializer.deserializeInto(this, state.getChild(StudySerializationUtils.Xml.MAIN_ELEMENT)); + deserialize(state); VERSION = CURRENT_VERSION; if (myCourse != null) { myCourse.initCourse(true); - if (version != VERSION) { - final File updatedCourse = new File(StudyProjectGenerator.OUR_COURSES_DIR, myCourse.getName()); - if (updatedCourse.exists()) { - myCourse.setCourseDirectory(updatedCourse.getAbsolutePath()); - } - } } } catch (StudySerializationUtils.StudyUnrecognizedFormatException e) { @@ -163,18 +189,44 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb } } + private void updateTestHelper() { + StartupManager.getInstance(myProject).runWhenProjectIsInitialized(() -> ApplicationManager.getApplication().runWriteAction(() -> { + final VirtualFile testHelper = myProject.getBaseDir().findChild(EduNames.TEST_HELPER); + if (testHelper != null) { + StudyUtils.deleteFile(testHelper); + } + final FileTemplate template = + FileTemplateManager.getInstance(myProject).getInternalTemplate(FileUtil.getNameWithoutExtension(EduNames.TEST_HELPER)); + try { + final PsiDirectory projectDir = PsiManager.getInstance(myProject).findDirectory(myProject.getBaseDir()); + if (projectDir != null) { + FileTemplateUtil.createFromTemplate(template, EduNames.TEST_HELPER, null, projectDir); + } + } + catch (Exception e) { + LOG.warn("Failed to create new test helper"); + } + })); + } + + private void deserialize(Element state) throws StudySerializationUtils.StudyUnrecognizedFormatException { + final Element taskManagerElement = state.getChild(StudySerializationUtils.Xml.MAIN_ELEMENT); + if (taskManagerElement == null) + throw new StudySerializationUtils.StudyUnrecognizedFormatException(); + XmlSerializer.deserializeInto(this, taskManagerElement); + final Element xmlCourse = StudySerializationUtils.Xml.getChildWithName(taskManagerElement, StudySerializationUtils.COURSE); + final Element remoteCourseElement = xmlCourse.getChild(REMOTE_COURSE); + if (remoteCourseElement != null) { + final RemoteCourse remoteCourse = new RemoteCourse(); + XmlSerializer.deserializeInto(remoteCourse, remoteCourseElement); + myCourse = remoteCourse; + } + } + public static StudyTaskManager getInstance(@NotNull final Project project) { return ServiceManager.getService(project, StudyTaskManager.class); } - public void addInvisibleFiles(String filePath) { - myInvisibleFiles.add(filePath); - } - - public boolean isInvisibleFile(String path) { - return myInvisibleFiles.contains(path); - } - public boolean shouldUseJavaFx() { return myShouldUseJavaFx; } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudyUtils.java b/python/educational-core/src/com/jetbrains/edu/learning/StudyUtils.java index a7ee1743ccee..8909c6936fd7 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/StudyUtils.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/StudyUtils.java @@ -7,6 +7,7 @@ import com.intellij.ide.IdeView; import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.ide.fileTemplates.FileTemplateManager; import com.intellij.ide.fileTemplates.FileTemplateUtil; +import com.intellij.ide.projectView.ProjectView; import com.intellij.ide.util.EditorHelper; import com.intellij.lang.Language; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -40,8 +41,9 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.newvfs.NewVirtualFile; +import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowAnchor; @@ -49,6 +51,7 @@ import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; import com.intellij.ui.JBColor; import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.content.Content; @@ -72,9 +75,7 @@ import com.jetbrains.edu.learning.courseFormat.tasks.ChoiceTask; import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks; import com.jetbrains.edu.learning.courseFormat.tasks.TheoryTask; -import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; import com.jetbrains.edu.learning.editor.StudyEditor; -import com.jetbrains.edu.learning.stepic.StepicUser; import com.jetbrains.edu.learning.ui.StudyToolWindow; import com.jetbrains.edu.learning.ui.StudyToolWindowFactory; import com.petebevin.markdown.MarkdownProcessor; @@ -84,9 +85,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.io.*; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; +import java.util.*; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @@ -238,20 +237,6 @@ public class StudyUtils { } } - public static File copyResourceFile(@NotNull final String sourceName, @NotNull final String copyName, @NotNull final Project project, - @NotNull final Task task) - throws IOException { - final StudyTaskManager taskManager = StudyTaskManager.getInstance(project); - final Course course = taskManager.getCourse(); - int taskNum = task.getIndex(); - int lessonNum = task.getLesson().getIndex(); - assert course != null; - final String pathToResource = FileUtil.join(course.getCourseDirectory(), EduNames.LESSON + lessonNum, EduNames.TASK + taskNum); - final File resourceFile = new File(pathToResource, copyName); - FileUtil.copy(new File(pathToResource, sourceName), resourceFile); - return resourceFile; - } - @Nullable public static Sdk findSdk(@NotNull final Task task, @NotNull final Project project) { final Language language = task.getLesson().getCourse().getLanguageById(); @@ -413,34 +398,6 @@ public class StudyUtils { } } - - @Nullable - public static VirtualFile getPatternFile(@NotNull TaskFile taskFile, String name) { - Task task = taskFile.getTask(); - String lessonDir = EduNames.LESSON + String.valueOf(task.getLesson().getIndex()); - String taskDir = EduNames.TASK + String.valueOf(task.getIndex()); - Course course = task.getLesson().getCourse(); - File resourceFile = new File(course.getCourseDirectory()); - if (!resourceFile.exists()) { - return null; - } - String patternPath = FileUtil.join(resourceFile.getPath(), lessonDir, taskDir, name); - VirtualFile patternFile = VfsUtil.findFileByIoFile(new File(patternPath), true); - if (patternFile == null) { - return null; - } - return patternFile; - } - - @Nullable - public static Document getPatternDocument(@NotNull final TaskFile taskFile, String name) { - VirtualFile patternFile = getPatternFile(taskFile, name); - if (patternFile == null) { - return null; - } - return FileDocumentManager.getInstance().getDocument(patternFile); - } - public static boolean isRenameableOrMoveable(@NotNull final Project project, @NotNull final Course course, @NotNull final PsiElement element) { if (element instanceof PsiFile) { VirtualFile virtualFile = ((PsiFile)element).getVirtualFile(); @@ -497,7 +454,6 @@ public class StudyUtils { String text = task.getTaskDescription() != null ? task.getTaskDescription() : getTaskTextByTaskName(task, taskDirectory); if (text == null) return null; - text = convertToHtml(text); if (course.isAdaptive()) text = wrapAdaptiveCourseText(task, text); return wrapTextToDisplayLatex(text); @@ -537,20 +493,33 @@ public class StudyUtils { @Nullable private static String getTaskTextByTaskName(@NotNull Task task, @Nullable VirtualFile taskDirectory) { if (taskDirectory == null) return null; - final String taskFileNameMd = constructTaskTextFilename(task, EduNames.TASK_MD); - final String taskFileNameHtml = constructTaskTextFilename(task, EduNames.TASK_HTML); - VirtualFile taskTextFile = ObjectUtils.chooseNotNull(taskDirectory.findChild(taskFileNameMd), taskDirectory.findChild(taskFileNameHtml)); - - if (taskTextFile == null) { - VirtualFile srcDir = taskDirectory.findChild(EduNames.SRC); - if (srcDir != null) { - taskTextFile = ObjectUtils.chooseNotNull(srcDir.findChild(taskFileNameHtml), srcDir.findChild(taskFileNameMd)); - } + String textFromHtmlFile = getTextByTaskFileFormat(task, taskDirectory, EduNames.TASK_HTML); + if (textFromHtmlFile != null) { + return textFromHtmlFile; } + + String taskTextFromMd = getTextByTaskFileFormat(task, taskDirectory, EduNames.TASK_HTML); + return convertToHtml(taskTextFromMd); + } + + @Nullable + private static String getTextByTaskFileFormat(@NotNull Task task, @NotNull VirtualFile taskDirectory, @NotNull String taskTextFileName) { + String textFilename = constructTaskTextFilename(task, taskTextFileName); + VirtualFile taskTextFile = taskDirectory.findChild(taskTextFileName); + if (taskTextFile != null) { return String.valueOf(LoadTextUtil.loadText(taskTextFile)); } + + VirtualFile srcDir = taskDirectory.findChild(EduNames.SRC); + if (srcDir != null) { + VirtualFile taskTextSrcFile = srcDir.findChild(textFilename); + if (taskTextSrcFile != null) { + return String.valueOf(LoadTextUtil.loadText(taskTextSrcFile)); + } + } + return null; } @@ -611,23 +580,6 @@ public class StudyUtils { return course != null && EduNames.STUDY.equals(course.getCourseMode()); } - @NotNull - public static File getCourseDirectory(Course course) { - final File courseDirectory; - if (course.isAdaptive()) { - StepicUser user = StudySettings.getInstance().getUser(); - String email = user == null ? "" : user.getEmail(); - - courseDirectory = new File(StudyProjectGenerator.OUR_COURSES_DIR, - StudyProjectGenerator.ADAPTIVE_COURSE_PREFIX + course.getName() - + "_" + email); - } - else { - courseDirectory = new File(StudyProjectGenerator.OUR_COURSES_DIR, course.getName()); - } - return courseDirectory; - } - public static boolean hasJavaFx() { try { Class.forName("javafx.application.Platform"); @@ -713,7 +665,9 @@ public class StudyUtils { return task; } - private static String convertToHtml(@NotNull final String content) { + @Nullable + private static String convertToHtml(@Nullable final String content) { + if (content == null) return null; ArrayList lines = ContainerUtil.newArrayList(content.split("\n|\r|\r\n")); MarkdownUtil.replaceHeaders(lines); MarkdownUtil.replaceCodeBlock(lines); @@ -866,4 +820,48 @@ public class StudyUtils { LOG.error(e); } } + public static void openFirstTask(@NotNull final Course course, @NotNull final Project project) { + LocalFileSystem.getInstance().refresh(false); + final Lesson firstLesson = getFirst(course.getLessons()); + if (firstLesson == null) return; + final Task firstTask = getFirst(firstLesson.getTaskList()); + if (firstTask == null) return; + final VirtualFile taskDir = firstTask.getTaskDir(project); + if (taskDir == null) return; + final Map taskFiles = firstTask.getTaskFiles(); + VirtualFile activeVirtualFile = null; + for (Map.Entry entry : taskFiles.entrySet()) { + final String relativePath = entry.getKey(); + final TaskFile taskFile = entry.getValue(); + taskDir.refresh(false, true); + final VirtualFile virtualFile = taskDir.findFileByRelativePath(relativePath); + if (virtualFile != null) { + if (!taskFile.getActivePlaceholders().isEmpty()) { + activeVirtualFile = virtualFile; + } + } + } + if (activeVirtualFile != null) { + final PsiFile file = PsiManager.getInstance(project).findFile(activeVirtualFile); + ProjectView.getInstance(project).select(file, activeVirtualFile, false); + final FileEditor[] editors = FileEditorManager.getInstance(project).openFile(activeVirtualFile, true); + if (editors.length == 0) { + return; + } + final FileEditor studyEditor = editors[0]; + if (studyEditor instanceof StudyEditor) { + selectFirstAnswerPlaceholder((StudyEditor)studyEditor, project); + } + FileEditorManager.getInstance(project).openFile(activeVirtualFile, true); + } + else { + String first = getFirst(taskFiles.keySet()); + if (first != null) { + NewVirtualFile firstFile = ((VirtualDirectoryImpl)taskDir).refreshAndFindChild(first); + if (firstFile != null) { + FileEditorManager.getInstance(project).openFile(firstFile, true); + } + } + } + } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRefreshAnswerPlaceholder.java b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRefreshAnswerPlaceholder.java index 85f3d3869ae3..8c6ea0bd36cf 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRefreshAnswerPlaceholder.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRefreshAnswerPlaceholder.java @@ -3,13 +3,9 @@ package com.jetbrains.edu.learning.actions; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.Presentation; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.command.CommandProcessor; -import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.TextRange; import com.jetbrains.edu.learning.StudyState; import com.jetbrains.edu.learning.StudySubtaskUtils; import com.jetbrains.edu.learning.StudyTaskManager; @@ -17,8 +13,8 @@ import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.StudyStatus; import com.jetbrains.edu.learning.courseFormat.TaskFile; -import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks; import com.jetbrains.edu.learning.editor.StudyEditor; import org.jetbrains.annotations.Nullable; @@ -41,24 +37,12 @@ public class StudyRefreshAnswerPlaceholder extends DumbAwareAction { return; } StudyEditor studyEditor = StudyUtils.getSelectedStudyEditor(project); - final StudyState studyState = new StudyState(studyEditor); - if (answerPlaceholder.getTaskFile().getTask() instanceof TaskWithSubtasks) { - StudySubtaskUtils.refreshPlaceholder(studyState.getEditor(), answerPlaceholder); - return; + if (studyEditor != null) { + StudySubtaskUtils.refreshPlaceholder(studyEditor.getEditor(), answerPlaceholder); + final StudyTaskManager taskManager = StudyTaskManager.getInstance(project); + answerPlaceholder.reset(); + taskManager.setStatus(answerPlaceholder, StudyStatus.Unchecked); } - Document patternDocument = StudyUtils.getPatternDocument(answerPlaceholder.getTaskFile(), studyState.getVirtualFile().getName()); - if (patternDocument == null) { - return; - } - AnswerPlaceholder.MyInitialState initialState = answerPlaceholder.getInitialState(); - int startOffset = initialState.getOffset(); - final String text = patternDocument.getText(new TextRange(startOffset, startOffset + initialState.getLength())); - CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> { - Document document = studyState.getEditor().getDocument(); - int offset = answerPlaceholder.getOffset(); - document.deleteString(offset, offset + answerPlaceholder.getRealLength()); - document.insertString(offset, text); - }), NAME, null); } @Override diff --git a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java index 08fb50fcfb43..5cbb7c749ecd 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java @@ -76,7 +76,7 @@ public class StudyRefreshTaskFileAction extends StudyActionWithShortcut { } } else { - if (!resetTaskFile(editor.getDocument(), project, taskFile, studyState.getVirtualFile().getName())) { + if (!resetTaskFile(editor.getDocument(), project, taskFile)) { Messages.showInfoMessage("The initial text of task file is unavailable", "Failed to Refresh Task File"); return; } @@ -95,16 +95,13 @@ public class StudyRefreshTaskFileAction extends StudyActionWithShortcut { () -> IdeFocusManager.getInstance(project).requestFocus(editor.getContentComponent(), true)); StudyNavigator.navigateToFirstAnswerPlaceholder(editor, taskFile); - showBalloon(project, "You can start again now", MessageType.INFO); + showBalloon(project, MessageType.INFO); } private static boolean resetTaskFile(@NotNull final Document document, @NotNull final Project project, - TaskFile taskFile, - String name) { - if (!resetDocument(document, taskFile, name)) { - return false; - } + TaskFile taskFile) { + resetDocument(document, taskFile); final Task task = taskFile.getTask(); task.setStatus(StudyStatus.Unchecked); if (task instanceof ChoiceTask) { @@ -116,9 +113,9 @@ public class StudyRefreshTaskFileAction extends StudyActionWithShortcut { return true; } - private static void showBalloon(@NotNull final Project project, String text, @NotNull final MessageType messageType) { + private static void showBalloon(@NotNull final Project project, @NotNull final MessageType messageType) { BalloonBuilder balloonBuilder = - JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(text, messageType, null); + JBPopupFactory.getInstance().createHtmlTextBalloonBuilder("You can start again now", messageType, null); final Balloon balloon = balloonBuilder.createBalloon(); StudyEditor selectedStudyEditor = StudyUtils.getSelectedStudyEditor(project); assert selectedStudyEditor != null; @@ -135,19 +132,14 @@ public class StudyRefreshTaskFileAction extends StudyActionWithShortcut { } - private static boolean resetDocument(@NotNull final Document document, - @NotNull final TaskFile taskFile, - String fileName) { - final Document patternDocument = StudyUtils.getPatternDocument(taskFile, fileName); - if (patternDocument == null) { - return false; - } + private static void resetDocument(@NotNull final Document document, + @NotNull final TaskFile taskFile) { StudyUtils.deleteGuardedBlocks(document); taskFile.setTrackChanges(false); clearDocument(document); - document.setText(patternDocument.getCharsSequence()); + + document.setText(taskFile.text); taskFile.setTrackChanges(true); - return true; } private static void clearDocument(@NotNull final Document document) { diff --git a/python/educational-core/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java b/python/educational-core/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java index 3604bd8665c0..daa1d3491eb5 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java @@ -21,7 +21,6 @@ import com.jetbrains.edu.learning.courseFormat.StudyStatus; import com.jetbrains.edu.learning.courseFormat.TaskFile; import org.jetbrains.annotations.NotNull; -import java.io.File; import java.io.IOException; public class StudySmartChecker { @@ -40,7 +39,6 @@ public class StudySmartChecker { @NotNull final VirtualFile virtualFile, @NotNull final Document usersDocument) { VirtualFile fileWindows = null; - File resourceFile = null; VirtualFile windowCopy = null; try { final int index = placeholder.getIndex(); @@ -49,7 +47,6 @@ public class StudySmartChecker { final FileDocumentManager documentManager = FileDocumentManager.getInstance(); final Document windowDocument = documentManager.getDocument(windowCopy); if (windowDocument != null) { - resourceFile = StudyUtils.copyResourceFile(virtualFile.getName(), windowCopy.getName(), project, usersTaskFile.getTask()); TaskFile windowTaskFile = answerTaskFile.getTask().copy().getTaskFile(StudyUtils.pathRelativeToTask(virtualFile)); if (windowTaskFile == null) { return; @@ -81,9 +78,6 @@ public class StudySmartChecker { finally { StudyUtils.deleteFile(windowCopy); StudyUtils.deleteFile(fileWindows); - if (resourceFile != null && resourceFile.exists() && !resourceFile.delete()) { - LOG.error("failed to delete", resourceFile.getPath()); - } } } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/core/EduUtils.java b/python/educational-core/src/com/jetbrains/edu/learning/core/EduUtils.java index 0f6a8e5fe719..0ec946b2aefe 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/core/EduUtils.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/core/EduUtils.java @@ -13,18 +13,20 @@ import com.intellij.openapi.command.undo.UndoableAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileTypes.PlainTextFileType; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.psi.PsiDirectory; +import com.intellij.testFramework.LightVirtualFile; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.courseFormat.*; import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks; +import org.apache.commons.codec.binary.Base64; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -120,68 +122,52 @@ public class EduUtils { VirtualFileManager.getInstance().refreshWithoutFileWatcher(true); } - - public static VirtualFile copyFile(Object requestor, VirtualFile toDir, VirtualFile file) { - Document document = FileDocumentManager.getInstance().getDocument(file); - if (document != null) { - FileDocumentManager.getInstance().saveDocument(document); - } - String taskRelativePath = StudyUtils.pathRelativeToTask(file); - try { - VirtualFile userFile = toDir.findFileByRelativePath(taskRelativePath); - if (userFile != null) { - userFile.delete(requestor); - } - return VfsUtil.copyFileRelative(requestor, file, toDir, taskRelativePath); - } - catch (IOException e) { - LOG.info("Failed to create file " + taskRelativePath + " in folder " + toDir.getPath(), e); - } - return null; - } - @Nullable - public static Pair createStudentFile(Object requestor, - Project project, - VirtualFile answerFile, - VirtualFile parentDir, - @Nullable Task task, - int targetSubtaskIndex) { - - VirtualFile studentFile = copyFile(requestor, parentDir, answerFile); - if (studentFile == null) { - return null; - } - Document studentDocument = FileDocumentManager.getInstance().getDocument(studentFile); - if (studentDocument == null) { - return null; - } - if (task == null) { - task = StudyUtils.getTaskForFile(project, answerFile); + public static TaskFile createStudentFile(Project project, VirtualFile answerFile, @Nullable Task task, int targetSubtaskIndex) { + try { if (task == null) { + task = StudyUtils.getTaskForFile(project, answerFile); + if (task == null) { + return null; + } + task = task.copy(); + } + TaskFile taskFile = task.getTaskFile(StudyUtils.pathRelativeToTask(answerFile)); + if (taskFile == null) { return null; } - task = task.copy(); - } - TaskFile taskFile = task.getTaskFile(StudyUtils.pathRelativeToTask(answerFile)); - if (taskFile == null) { - return null; - } - EduDocumentListener listener = new EduDocumentListener(taskFile, false); - studentDocument.addDocumentListener(listener); - taskFile.setTrackLengths(false); - for (AnswerPlaceholder placeholder : taskFile.getAnswerPlaceholders()) { - if (task instanceof TaskWithSubtasks) { - int fromSubtask = ((TaskWithSubtasks)task).getActiveSubtaskIndex(); - placeholder.switchSubtask(studentDocument, fromSubtask, targetSubtaskIndex); + if (isImage(taskFile.name)) { + taskFile.text = Base64.encodeBase64String(answerFile.contentsToByteArray()); + return taskFile; } + final LightVirtualFile studentFile = new LightVirtualFile("student_task", PlainTextFileType.INSTANCE, + VfsUtilCore.loadText(answerFile)); + Document studentDocument = FileDocumentManager.getInstance().getDocument(studentFile); + if (studentDocument == null) { + return null; + } + EduDocumentListener listener = new EduDocumentListener(taskFile, false); + studentDocument.addDocumentListener(listener); + taskFile.setTrackLengths(false); + for (AnswerPlaceholder placeholder : taskFile.getAnswerPlaceholders()) { + if (task instanceof TaskWithSubtasks) { + int fromSubtask = ((TaskWithSubtasks)task).getActiveSubtaskIndex(); + placeholder.switchSubtask(studentDocument, fromSubtask, targetSubtaskIndex); + } + } + for (AnswerPlaceholder placeholder : taskFile.getAnswerPlaceholders()) { + replaceWithTaskText(studentDocument, placeholder, targetSubtaskIndex); + } + taskFile.setTrackChanges(true); + studentDocument.removeDocumentListener(listener); + taskFile.text = studentDocument.getImmutableCharSequence().toString(); + return taskFile; } - for (AnswerPlaceholder placeholder : taskFile.getAnswerPlaceholders()) { - replaceWithTaskText(studentDocument, placeholder, targetSubtaskIndex); + catch (IOException e) { + LOG.error("Failed to convert answer file to student one"); } - taskFile.setTrackChanges(true); - studentDocument.removeDocumentListener(listener); - return Pair.create(studentFile, taskFile); + + return null; } private static void replaceWithTaskText(Document studentDocument, AnswerPlaceholder placeholder, int toSubtaskIndex) { diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/Course.java b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/Course.java index 392ce6b4c6b2..9e4efbe46f33 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/Course.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/Course.java @@ -8,35 +8,32 @@ import com.intellij.util.xmlb.XmlSerializer; import com.intellij.util.xmlb.annotations.Transient; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; -import com.jetbrains.edu.learning.courseFormat.tasks.Task; -import com.jetbrains.edu.learning.stepic.EduStepicConnector; import com.jetbrains.edu.learning.stepic.StepicUser; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; -import java.util.Date; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; public class Course { - @Expose private List lessons = new ArrayList<>(); - @Expose private List authors = new ArrayList<>(); - @Expose private String description; - @Expose private String name; - private String myCourseDirectory = ""; - @Expose private int id; - @Expose @SerializedName("update_date") private Date myUpdateDate; - @Expose private boolean isAdaptive = false; - @Expose @SerializedName("language") private String myLanguage = "Python"; + public static final Course INVALID_COURSE = new Course(); + + @Expose protected List lessons = new ArrayList<>(); + transient private List authors = new ArrayList<>(); + @Expose @SerializedName("summary") private String description; + @Expose @SerializedName("title") private String name; + + transient private String myLanguage = "Python"; //this field is used to distinguish ordinary and CheckIO projects, //"PyCharm" is used here for historical reasons private String courseType = EduNames.PYCHARM; - private String courseMode = EduNames.STUDY; //this field is used to distinguish study and course creator modes + protected String courseMode = EduNames.STUDY; //this field is used to distinguish study and course creator modes - public Course() { - } + public Course() {} /** * Initializes state of course @@ -48,12 +45,18 @@ public class Course { } public List getLessons() { - return lessons; + return getLessons(false); + } + + public List getLessons(boolean withAdditional) { + return withAdditional ? lessons + : lessons.stream().filter(lesson -> !EduNames.PYCHARM_ADDITIONAL.equals(lesson.getName())).collect(Collectors.toList()); } public void setLessons(List lessons) { this.lessons = lessons; } + public void addLessons(List lessons) { this.lessons.addAll(lessons); } @@ -62,9 +65,12 @@ public class Course { lessons.add(lesson); } + public void removeLesson(Lesson lesson) { + lessons.remove(lesson); + } + public Lesson getLesson(@NotNull final String name) { int lessonIndex = EduUtils.getIndex(name, EduNames.LESSON); - List lessons = getLessons(); if (!EduUtils.indexIsValid(lessonIndex, lessons)) { return null; } @@ -118,14 +124,6 @@ public class Course { this.name = name; } - public String getCourseDirectory() { - return myCourseDirectory; - } - - public void setCourseDirectory(@NotNull final String courseDirectory) { - myCourseDirectory = courseDirectory; - } - public String getDescription() { return description; } @@ -135,35 +133,10 @@ public class Course { } public boolean isUpToDate() { - if (id == 0) return true; - if (!EduNames.STUDY.equals(courseMode)) return true; - final Date date = EduStepicConnector.getCourseUpdateDate(id); - if (date == null) return true; - if (myUpdateDate == null) return true; - if (date.after(myUpdateDate)) return false; - for (Lesson lesson : lessons) { - if (!lesson.isUpToDate()) return false; - } return true; } - public void setUpdated() { - setUpdateDate(EduStepicConnector.getCourseUpdateDate(id)); - for (Lesson lesson : lessons) { - lesson.setUpdateDate(EduStepicConnector.getLessonUpdateDate(lesson.getId())); - for (Task task : lesson.getTaskList()) { - task.setUpdateDate(EduStepicConnector.getTaskUpdateDate(task.getStepId())); - } - } - } - - public void setUpdateDate(Date date) { - myUpdateDate = date; - } - - public Date getUpdateDate() { - return myUpdateDate; - } + public void setUpdated() {} public Language getLanguageById() { return Language.findLanguageByID(getLanguageID()); @@ -208,22 +181,6 @@ public class Course { this.courseType = courseType; } - public boolean isAdaptive() { - return isAdaptive; - } - - public void setAdaptive(boolean adaptive) { - isAdaptive = adaptive; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - public String getCourseMode() { return courseMode; } @@ -234,8 +191,21 @@ public class Course { public Course copy() { Element element = XmlSerializer.serialize(this); - Course copy = XmlSerializer.deserialize(element, Course.class); + Course copy = XmlSerializer.deserialize(element, getClass()); copy.initCourse(true); return copy; } + + public boolean isAdaptive() { + return false; + } + + public void sortLessons() { + Collections.sort(lessons, EduUtils.INDEX_COMPARATOR); + } + + @Override + public String toString() { + return getName(); + } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/CourseInfo.java b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/CourseInfo.java deleted file mode 100644 index acac3c5f951d..000000000000 --- a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/CourseInfo.java +++ /dev/null @@ -1,176 +0,0 @@ -package com.jetbrains.edu.learning.courseFormat; - -import com.google.gson.annotations.SerializedName; -import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; -import com.jetbrains.edu.learning.stepic.EduStepicConnector; -import com.jetbrains.edu.learning.stepic.StepicUser; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Implementation of class which contains information to be shawn in course description in tool window - * and when project is being created - */ -public class CourseInfo { - public static final CourseInfo INVALID_COURSE = new CourseInfo(); - - @SerializedName("title") private String myName; - int id; - boolean isAdaptive; - @SerializedName("is_public") boolean isPublic; - List sections; - List instructors = new ArrayList<>(); - - List myAuthors = new ArrayList<>(); - @SerializedName("summary") private String myDescription; - @SerializedName("course_format") private String myType = "pycharm" + EduStepicConnector.CURRENT_VERSION + " Python"; //course type in format "pycharm " - @Nullable private String username; - - @SerializedName("update_date") private Date updateDate; - @SerializedName("is_idea_compatible") private boolean isCompatible = true; - - public String getName() { - return myName; - } - - @NotNull - public List getAuthors() { - return myAuthors; - } - - public String getDescription() { - return myDescription; - } - - public String getType() { - return myType; - } - - @Override - public String toString() { - return getName(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - CourseInfo that = (CourseInfo)o; - final String thatName = that.getName(); - final String thatDescription = that.getDescription(); - if (thatName == null && getName() == null && thatDescription == null && - getDescription() == null) { - return true; - } - if (thatName == null || thatDescription == null) { - return false; - } - return thatName.equals(getName()) - && thatDescription.equals(myDescription); - } - - @Override - public int hashCode() { - int result = getName() != null ? getName().hashCode() : 0; - result = 31 * result + (myDescription != null ? myDescription.hashCode() : 0); - return result; - } - - @Nullable - public String getUsername() { - return username; - } - - public void setUsername(@Nullable String username) { - this.username = username; - } - - public void setName(String name) { - myName = name; - } - - public void setAuthors(List authors) { - myAuthors = authors; - for (StepicUser author : authors) { - if (author.getId() > 0) { - instructors.add(author.getId()); - } - } - } - - public void addAuthor(StepicUser author) { - if (myAuthors == null) { - myAuthors = new ArrayList<>(); - } - myAuthors.add(author); - } - - public void setDescription(String description) { - myDescription = description; - } - - public void setType(String type) { - myType = type; - } - - public boolean isAdaptive() { - return isAdaptive; - } - - public void setAdaptive(boolean adaptive) { - isAdaptive = adaptive; - } - - public boolean isPublic() { - return isPublic; - } - - public void setPublic(boolean aPublic) { - isPublic = aPublic; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public Date getUpdateDate() { - return updateDate; - } - - public void setUpdateDate(Date updateDate) { - this.updateDate = updateDate; - } - - public static CourseInfo fromCourse(@Nullable final Course course) { - if (course == null) return null; - final List infos = StudyProjectGenerator.getCoursesFromCache().stream(). - filter(info -> info.id == course.getId()).collect(Collectors.toList()); - if (infos.isEmpty()) return null; - return infos.get(0); - } - - public List getSections() { - return sections; - } - - public boolean isCompatible() { - return isCompatible; - } - - public void setCompatible(boolean compatible) { - isCompatible = compatible; - } - - public List getInstructors() { - return instructors; - } -} diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/RemoteCourse.java b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/RemoteCourse.java new file mode 100644 index 000000000000..01e764610e62 --- /dev/null +++ b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/RemoteCourse.java @@ -0,0 +1,98 @@ +package com.jetbrains.edu.learning.courseFormat; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.courseFormat.tasks.Task; +import com.jetbrains.edu.learning.stepic.EduStepicConnector; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class RemoteCourse extends Course { + //course type in format "pycharm " + @SerializedName("course_format") private String myType = "pycharm" + EduStepicConnector.CURRENT_VERSION + " Python"; + @SerializedName("is_idea_compatible") private boolean isCompatible = true; + List sections; + List instructors = new ArrayList<>(); + @Expose private int id; + @Expose @SerializedName("update_date") private Date myUpdateDate; + @Expose private boolean isAdaptive = false; + @SerializedName("is_public") boolean isPublic; + + public String getType() { + return myType; + } + + public List getSections() { + return sections; + } + + public void setSections(List sections) { + this.sections = sections; + } + + public void setInstructors(List instructors) { + this.instructors = instructors; + } + + public List getInstructors() { + return instructors; + } + + + public boolean isUpToDate() { + if (id == 0) return true; + if (!EduNames.STUDY.equals(courseMode)) return true; + final Date date = EduStepicConnector.getCourseUpdateDate(id); + if (date == null) return true; + if (myUpdateDate == null) return true; + if (date.after(myUpdateDate)) return false; + for (Lesson lesson : lessons) { + if (!lesson.isUpToDate()) return false; + } + return true; + } + + public void setUpdated() { + setUpdateDate(EduStepicConnector.getCourseUpdateDate(id)); + for (Lesson lesson : lessons) { + lesson.setUpdateDate(EduStepicConnector.getLessonUpdateDate(lesson.getId())); + for (Task task : lesson.getTaskList()) { + task.setUpdateDate(EduStepicConnector.getTaskUpdateDate(task.getStepId())); + } + } + } + + public void setUpdateDate(Date date) { + myUpdateDate = date; + } + + public Date getUpdateDate() { + return myUpdateDate; + } + + public boolean isAdaptive() { + return isAdaptive; + } + + public void setAdaptive(boolean adaptive) { + isAdaptive = adaptive; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public void copyCourseParameters(RemoteCourse course) { + setName(course.getName()); + + setUpdateDate(course.getUpdateDate()); + + } +} diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/Task.java b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/Task.java index d5b56e06dd01..e3561075e1bf 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/Task.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/Task.java @@ -21,6 +21,11 @@ import java.util.Map; /** * Implementation of task which contains task files, tests, input file for tests + * + * To implement new task there are 3 steps to be done: + * - extend Task class + * - go to Lesson and update elementTypes in taskList AbstractCollection. Needed for proper xml serialization + * - Update TaskSerializer and TaskDeserializer in StudySerializationUtil to handle json serialization */ public class Task implements StudyItem { @Expose private String name; @@ -35,8 +40,10 @@ public class Task implements StudyItem { @SerializedName("task_files") @Expose public Map taskFiles = new HashMap<>(); - protected Map testsText = new HashMap<>(); - protected Map taskTexts = new HashMap<>(); + @SerializedName("test_files") + @Expose protected Map testsText = new HashMap<>(); + @SerializedName("task_texts") + @Expose protected Map taskTexts = new HashMap<>(); @Transient private Lesson myLesson; @Expose @SerializedName("update_date") private Date myUpdateDate; @@ -105,10 +112,6 @@ public class Task implements StudyItem { return name != null ? taskFiles.get(name) : null; } - public boolean isTaskFile(@NotNull final String fileName) { - return taskFiles.get(fileName) != null; - } - public void addTaskFile(@NotNull final String name, int index) { TaskFile taskFile = new TaskFile(); taskFile.setIndex(index); @@ -226,7 +229,7 @@ public class Task implements StudyItem { public Task copy() { Element element = XmlSerializer.serialize(this); - Task copy = XmlSerializer.deserialize(element, Task.class); + Task copy = XmlSerializer.deserialize(element, getClass()); copy.initTask(null, true); return copy; } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java b/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java index e19a67d25430..dad0e77a7d64 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java @@ -1,125 +1,33 @@ package com.jetbrains.edu.learning.courseGeneration; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.jetbrains.edu.learning.StudyTaskManager; -import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.courseFormat.tasks.Task; +import org.apache.commons.codec.binary.Base64; import org.jetbrains.annotations.NotNull; -import java.io.File; import java.io.IOException; import java.util.List; import java.util.Map; public class StudyGenerator { - private StudyGenerator() { - - } private static final Logger LOG = Logger.getInstance(StudyGenerator.class.getName()); - /** - * Creates task files in its task folder in project user created - * - * @param taskDir project directory of task which task file belongs to - * @param resourceRoot directory where original task file stored - * @throws IOException - */ - public static void createTaskFile(@NotNull final VirtualFile taskDir, @NotNull final File resourceRoot, - @NotNull final String name) throws IOException { - String systemIndependentName = FileUtil.toSystemIndependentName(name); - File resourceFile = new File(resourceRoot, name); - File fileInProject = new File(taskDir.getPath(), systemIndependentName); - FileUtil.copyFileOrDir(resourceFile, fileInProject); - } - - /** - * Creates task directory in its lesson folder in project user created - * - * @param lessonDir project directory of lesson which task belongs to - * @param resourceRoot directory where original task file stored - * @throws IOException - */ - public static void createTask(@NotNull final Task task, @NotNull final VirtualFile lessonDir, @NotNull final File resourceRoot, - @NotNull final Project project) throws IOException { - VirtualFile taskDir = lessonDir.createChildDirectory(project, EduNames.TASK + Integer.toString(task.getIndex())); - File newResourceRoot = new File(resourceRoot, taskDir.getName()); - int i = 0; - for (Map.Entry taskFile : task.getTaskFiles().entrySet()) { - TaskFile taskFileContent = taskFile.getValue(); - taskFileContent.setIndex(i); - i++; - createTaskFile(taskDir, newResourceRoot, taskFile.getKey()); - } - File[] filesInTask = newResourceRoot.listFiles(); - if (filesInTask != null) { - for (File file : filesInTask) { - String fileName = file.getName(); - if (!task.isTaskFile(fileName)) { - File resourceFile = new File(newResourceRoot, fileName); - File fileInProject = new File(taskDir.getCanonicalPath(), fileName); - FileUtil.copyFileOrDir(resourceFile, fileInProject); - if (!StudyUtils.isTestsFile(project, fileName) && !StudyUtils.isTaskDescriptionFile(fileName)) { - StudyTaskManager.getInstance(project).addInvisibleFiles(FileUtil.toSystemIndependentName(fileInProject.getPath())); - } - } - } - } - } - - /** - * Creates lesson directory in its course folder in project user created - * - * @param courseDir project directory of course - * @param resourceRoot directory where original lesson stored - * @throws IOException - */ - public static void createLesson(@NotNull final Lesson lesson, @NotNull final VirtualFile courseDir, @NotNull final File resourceRoot, - @NotNull final Project project) throws IOException { - if (EduNames.PYCHARM_ADDITIONAL.equals(lesson.getName())) return; - String lessonDirName = EduNames.LESSON + Integer.toString(lesson.getIndex()); - VirtualFile lessonDir = courseDir.createChildDirectory(project, lessonDirName); - final List taskList = lesson.getTaskList(); - for (int i = 1; i <= taskList.size(); i++) { - Task task = taskList.get(i - 1); - task.setIndex(i); - createTask(task, lessonDir, new File(resourceRoot, lessonDir.getName()), project); - } - } - - /** - * Creates course directory in project user created - * - * @param baseDir project directory - * @param resourceRoot directory where original course is stored - */ - public static void createCourse(@NotNull final Course course, @NotNull final VirtualFile baseDir, @NotNull final File resourceRoot, - @NotNull final Project project) { + private StudyGenerator() {} + public static void createCourse(@NotNull final Course course, @NotNull final VirtualFile baseDir) { try { - final List lessons = course.getLessons(); + final List lessons = course.getLessons(true); for (int i = 1; i <= lessons.size(); i++) { Lesson lesson = lessons.get(i - 1); lesson.setIndex(i); - createLesson(lesson, baseDir, resourceRoot, project); - } - File[] files = resourceRoot.listFiles( - (dir, name) -> !name.contains(EduNames.LESSON) && !name.equals(EduNames.COURSE_META_FILE) && !name.equals(EduNames.HINTS)); - if (files != null) { - for (File file : files) { - File dir = new File(baseDir.getPath(), file.getName()); - if (file.isDirectory()) { - FileUtil.copyDir(file, dir); - continue; - } - FileUtil.copy(file, dir); - } + createLesson(lesson, baseDir); } } catch (IOException e) { @@ -127,4 +35,92 @@ public class StudyGenerator { } } + public static void createLesson(@NotNull final Lesson lesson, @NotNull final VirtualFile courseDir) throws IOException { + if (EduNames.PYCHARM_ADDITIONAL.equals(lesson.getName())) { + createAdditionalFiles(lesson, courseDir); + } + else { + String lessonDirName = EduNames.LESSON + Integer.toString(lesson.getIndex()); + VirtualFile lessonDir = courseDir.createChildDirectory(courseDir, lessonDirName); + final List taskList = lesson.getTaskList(); + for (int i = 1; i <= taskList.size(); i++) { + Task task = taskList.get(i - 1); + task.setIndex(i); + createTask(task, lessonDir); + } + } + } + + public static void createTask(@NotNull final Task task, @NotNull final VirtualFile lessonDir) throws IOException { + VirtualFile taskDir = lessonDir.createChildDirectory(lessonDir, EduNames.TASK + Integer.toString(task.getIndex())); + createTaskContent(task, taskDir); + } + + public static void createTaskContent(@NotNull Task task, VirtualFile taskDir) throws IOException { + int i = 0; + for (Map.Entry taskFile : task.getTaskFiles().entrySet()) { + TaskFile taskFileContent = taskFile.getValue(); + taskFileContent.setIndex(i); + i++; + createTaskFile(taskDir, taskFile.getValue()); + } + createTestFiles(taskDir, task); + createDescriptions(taskDir, task); + } + + public static void createTaskFile(@NotNull final VirtualFile taskDir, @NotNull final TaskFile taskFile) throws IOException { + final String name = taskFile.name; + createChildFile(taskDir, name, taskFile.text); + } + + public static void createDescriptions(VirtualFile taskDir, Task task) throws IOException { + final Map texts = task.getTaskTexts(); + for (Map.Entry entry : texts.entrySet()) { + final String name = entry.getKey(); + final VirtualFile virtualTaskFile = taskDir.createChildData(taskDir, name); + VfsUtil.saveText(virtualTaskFile, entry.getValue()); + } + } + + public static void createTestFiles(VirtualFile taskDir, Task task) throws IOException { + final Map tests = task.getTestsText(); + for (Map.Entry entry : tests.entrySet()) { + final String name = entry.getKey(); + final VirtualFile virtualTaskFile = taskDir.createChildData(taskDir, name); + VfsUtil.saveText(virtualTaskFile, entry.getValue()); + } + } + + private static void createAdditionalFiles(Lesson lesson, VirtualFile courseDir) throws IOException { + final List taskList = lesson.getTaskList(); + if (taskList.size() != 1) return; + final Task task = taskList.get(0); + for (Map.Entry entry : task.getTestsText().entrySet()) { + createChildFile(courseDir, entry.getKey(), entry.getValue()); + } + } + + + public static void createChildFile(@NotNull VirtualFile taskDir, String name, String text) throws IOException { + String newDirectories = null; + String fileName = name; + VirtualFile dir = taskDir; + if (name.contains("/")) { + int pos = name.lastIndexOf("/"); + fileName = name.substring(pos + 1); + newDirectories = name.substring(0, pos); + } + if (newDirectories != null) { + dir = VfsUtil.createDirectoryIfMissing(taskDir, newDirectories); + } + if (dir != null) { + final VirtualFile virtualTaskFile = dir.createChildData(taskDir, fileName); + if (EduUtils.isImage(name)) { + virtualTaskFile.setBinaryContent(Base64.decodeBase64(text)); + } + else { + VfsUtil.saveText(virtualTaskFile, text); + } + } + } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java b/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java index 86297e048d58..b294e5d29dad 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java @@ -1,66 +1,51 @@ package com.jetbrains.edu.learning.courseGeneration; -import com.google.gson.*; -import com.google.gson.stream.JsonReader; +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.intellij.facet.ui.ValidationResult; -import com.intellij.ide.projectView.ProjectView; +import com.intellij.lang.LanguageExtensionPoint; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.fileEditor.FileEditor; -import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileManager; -import com.intellij.openapi.vfs.newvfs.NewVirtualFile; -import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl; -import com.intellij.platform.templates.github.ZipUtil; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.io.zip.JBZipEntry; +import com.intellij.util.io.zip.JBZipFile; +import com.jetbrains.edu.learning.EduPluginConfigurator; import com.jetbrains.edu.learning.StudySerializationUtils; import com.jetbrains.edu.learning.StudySettings; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduNames; -import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.CourseInfo; -import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.courseFormat.RemoteCourse; import com.jetbrains.edu.learning.courseFormat.tasks.Task; -import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.statistics.EduUsagesCollector; import com.jetbrains.edu.learning.stepic.EduStepicConnector; import com.jetbrains.edu.learning.stepic.StepicUser; -import org.apache.commons.codec.binary.Base64; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.*; -import java.util.*; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import static com.jetbrains.edu.learning.StudyUtils.execCancelable; public class StudyProjectGenerator { - public static final String AUTHOR_ATTRIBUTE = "authors"; - public static final String LANGUAGE_ATTRIBUTE = "language"; - public static final String ADAPTIVE_COURSE_PREFIX = "__AdaptivePyCharmPython__"; - public static final File OUR_COURSES_DIR = new File(PathManager.getConfigPath(), "courses"); private static final Logger LOG = Logger.getInstance(StudyProjectGenerator.class.getName()); - private static final String COURSE_NAME_ATTRIBUTE = "name"; - private static final String COURSE_DESCRIPTION = "description"; - private static final String CACHE_NAME = "courseNames.txt"; private final List myListeners = ContainerUtil.newArrayList(); - private List myCourses = new ArrayList<>(); + private List myCourses = new ArrayList<>(); private List myEnrolledCoursesIds = new ArrayList<>(); - protected CourseInfo mySelectedCourseInfo; + protected Course mySelectedCourse; - public void setCourses(List courses) { + public void setCourses(List courses) { myCourses = courses; } @@ -78,12 +63,12 @@ public class StudyProjectGenerator { return myEnrolledCoursesIds; } - public void setSelectedCourse(@NotNull final CourseInfo courseName) { - mySelectedCourseInfo = courseName; + public void setSelectedCourse(@NotNull final Course course) { + mySelectedCourse = course; } - public CourseInfo getSelectedCourseInfo() { - return mySelectedCourseInfo; + public Course getSelectedCourse() { + return mySelectedCourse; } public void generateProject(@NotNull final Project project, @NotNull final VirtualFile baseDir) { @@ -97,50 +82,30 @@ public class StudyProjectGenerator { Messages.showWarningDialog("There is no recommended tasks for this adaptive course", "Error in Course Creation"); return; } - final File courseDirectory = StudyUtils.getCourseDirectory(course); StudyTaskManager.getInstance(project).setCourse(course); ApplicationManager.getApplication().runWriteAction(() -> { - StudyGenerator.createCourse(course, baseDir, courseDirectory, project); - course.setCourseDirectory(courseDirectory.getAbsolutePath()); - VirtualFileManager.getInstance().refreshWithoutFileWatcher(true); + StudyGenerator.createCourse(course, baseDir); StudyUtils.registerStudyToolWindow(course, project); - openFirstTask(course, project); + StudyUtils.openFirstTask(course, project); EduUsagesCollector.projectTypeCreated(course.isAdaptive() ? EduNames.ADAPTIVE : EduNames.STUDY); }); } @Nullable public Course getCourse(@NotNull final Project project) { - - final File courseFile = new File(new File(OUR_COURSES_DIR, mySelectedCourseInfo.getName()), EduNames.COURSE_META_FILE); - if (courseFile.exists()) { - final Course course = readCourseFromCache(courseFile, false); - if (course != null && course.isUpToDate()) { - return course; - } - return getCourseFromStepic(project); + if (mySelectedCourse instanceof RemoteCourse) { + return getCourseFromStepic(project, (RemoteCourse)mySelectedCourse); } - else { - final StepicUser user = StudySettings.getInstance().getUser(); - if (user != null) { - final File adaptiveCourseFile = new File(new File(OUR_COURSES_DIR, ADAPTIVE_COURSE_PREFIX + - mySelectedCourseInfo.getName() + "_" + - user.getEmail()), EduNames.COURSE_META_FILE); - if (adaptiveCourseFile.exists()) { - return readCourseFromCache(adaptiveCourseFile, true); - } - } - } - return getCourseFromStepic(project); + mySelectedCourse.initCourse(false); + return mySelectedCourse; } - private Course getCourseFromStepic(@NotNull Project project) { + private static RemoteCourse getCourseFromStepic(@NotNull Project project, RemoteCourse selectedCourse) { return ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); return execCancelable(() -> { - final Course course = EduStepicConnector.getCourse(project, mySelectedCourseInfo); + final RemoteCourse course = EduStepicConnector.getCourse(project, selectedCourse); if (StudyUtils.isCourseValid(course)) { - flushCourse(course); course.initCourse(false); } return course; @@ -148,281 +113,26 @@ public class StudyProjectGenerator { }, "Creating Course", true, project); } - @Nullable - private static Course readCourseFromCache(@NotNull File courseFile, boolean isAdaptive) { - Reader reader = null; - try { - reader = new InputStreamReader(new FileInputStream(courseFile), "UTF-8"); - Gson gson = - new GsonBuilder().registerTypeAdapter(Course.class, new StudySerializationUtils.Json.CourseTypeAdapter(courseFile)) - .registerTypeAdapter(Task.class, new StudySerializationUtils.Json.TaskDeserializer()).create(); - final Course course = gson.fromJson(reader, Course.class); - course.initCourse(isAdaptive); - return course; - } - catch (UnsupportedEncodingException | FileNotFoundException e) { - LOG.warn(e.getMessage()); - } - finally { - StudyUtils.closeSilently(reader); - } - return null; - } - - public static void openFirstTask(@NotNull final Course course, @NotNull final Project project) { - LocalFileSystem.getInstance().refresh(false); - final Lesson firstLesson = StudyUtils.getFirst(course.getLessons()); - if (firstLesson == null) return; - final Task firstTask = StudyUtils.getFirst(firstLesson.getTaskList()); - if (firstTask == null) return; - final VirtualFile taskDir = firstTask.getTaskDir(project); - if (taskDir == null) return; - final Map taskFiles = firstTask.getTaskFiles(); - VirtualFile activeVirtualFile = null; - for (Map.Entry entry : taskFiles.entrySet()) { - final String relativePath = entry.getKey(); - final TaskFile taskFile = entry.getValue(); - taskDir.refresh(false, true); - final VirtualFile virtualFile = taskDir.findFileByRelativePath(relativePath); - if (virtualFile != null) { - if (!taskFile.getActivePlaceholders().isEmpty()) { - activeVirtualFile = virtualFile; - } - } - } - if (activeVirtualFile != null) { - final PsiFile file = PsiManager.getInstance(project).findFile(activeVirtualFile); - ProjectView.getInstance(project).select(file, activeVirtualFile, false); - final FileEditor[] editors = FileEditorManager.getInstance(project).openFile(activeVirtualFile, true); - if (editors.length == 0) { - return; - } - final FileEditor studyEditor = editors[0]; - if (studyEditor instanceof StudyEditor) { - StudyUtils.selectFirstAnswerPlaceholder((StudyEditor)studyEditor, project); - } - FileEditorManager.getInstance(project).openFile(activeVirtualFile, true); - } - else { - String first = StudyUtils.getFirst(taskFiles.keySet()); - if (first != null) { - NewVirtualFile firstFile = ((VirtualDirectoryImpl)taskDir).refreshAndFindChild(first); - if (firstFile != null) { - FileEditorManager.getInstance(project).openFile(firstFile, true); - } - } - } - } - - public static void flushCourse(@NotNull final Course course) { - final File courseDirectory = StudyUtils.getCourseDirectory(course); - FileUtil.createDirectory(courseDirectory); - flushCourseJson(course, courseDirectory); - - int lessonIndex = 1; - List additionalLessons = new ArrayList<>(); - for (Lesson lesson : course.getLessons()) { - if (lesson.getName().equals(EduNames.PYCHARM_ADDITIONAL)) { - flushAdditionalFiles(courseDirectory, lesson); - additionalLessons.add(lesson); - } - else { - final File lessonDirectory = new File(courseDirectory, EduNames.LESSON + String.valueOf(lessonIndex)); - flushLesson(lessonDirectory, lesson); - lessonIndex += 1; - } - } - for (Lesson lesson : additionalLessons) { - course.getLessons().remove(lesson); - } - } - - private static void flushAdditionalFiles(File courseDirectory, Lesson lesson) { - final List taskList = lesson.getTaskList(); - if (taskList.size() != 1) return; - final Task task = taskList.get(0); - for (Map.Entry entry : task.getTestsText().entrySet()) { - final String name = entry.getKey(); - final String text = entry.getValue(); - final File file = new File(courseDirectory, name); - FileUtil.createIfDoesntExist(file); - try { - if (EduUtils.isImage(name)) { - FileUtil.writeToFile(file, Base64.decodeBase64(text)); - } - else { - FileUtil.writeToFile(file, text); - } - } - catch (IOException e) { - LOG.error("ERROR copying file " + name); - } - } - } - - public static void flushLesson(@NotNull final File lessonDirectory, @NotNull final Lesson lesson) { - FileUtil.createDirectory(lessonDirectory); - int taskIndex = 1; - for (Task task : lesson.taskList) { - final File taskDirectory = new File(lessonDirectory, EduNames.TASK + String.valueOf(taskIndex)); - flushTask(task, taskDirectory); - taskIndex += 1; - } - } - - public static void flushTask(@NotNull final Task task, @NotNull final File taskDirectory) { - FileUtil.createDirectory(taskDirectory); - for (Map.Entry taskFileEntry : task.taskFiles.entrySet()) { - final String name = FileUtil.toSystemDependentName(taskFileEntry.getKey()); - final TaskFile taskFile = taskFileEntry.getValue(); - final File file = new File(taskDirectory, name); - FileUtil.createIfDoesntExist(file); - - try { - if (EduUtils.isImage(taskFile.name)) { - FileUtil.writeToFile(file, Base64.decodeBase64(taskFile.text)); - } - else { - FileUtil.writeToFile(file, taskFile.text); - } - } - catch (IOException e) { - LOG.error(e); - } - } - createFiles(taskDirectory, task.getTestsText()); - createFiles(taskDirectory, task.getTaskTexts()); - } - - private static void createFiles(@NotNull File taskDirectory, Map files) { - for (Map.Entry entry : files.entrySet()) { - final File file = new File(taskDirectory, entry.getKey()); - if (file.exists()) { - FileUtil.delete(file); - } - FileUtil.createIfDoesntExist(file); - try { - FileUtil.writeToFile(file, entry.getValue()); - } - catch (IOException e) { - LOG.error(e); - } - } - } - - public static void flushCourseJson(@NotNull final Course course, @NotNull final File courseDirectory) { - final Gson gson = new GsonBuilder().setPrettyPrinting(). - registerTypeAdapter(Task.class, new StudySerializationUtils.Json.TaskSerializer()). - excludeFieldsWithoutExposeAnnotation().create(); - final String json = gson.toJson(course); - final File courseJson = new File(courseDirectory, EduNames.COURSE_META_FILE); - final FileOutputStream fileOutputStream; - try { - fileOutputStream = new FileOutputStream(courseJson); - OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8"); - try { - outputStreamWriter.write(json); - } - catch (IOException e) { - Messages.showErrorDialog(e.getMessage(), "Failed to Generate Json"); - LOG.info(e); - } - finally { - try { - outputStreamWriter.close(); - } - catch (IOException e) { - LOG.info(e); - } - } - } - catch (FileNotFoundException | UnsupportedEncodingException e) { - LOG.info(e); - } - } - - /** - * Writes courses to cache file {@link StudyProjectGenerator#CACHE_NAME} - */ - @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") - public static void flushCache(List courses) { - flushCache(courses, true); - } - - public static void flushCache(List courses, boolean preserveOld) { - File cacheFile = new File(OUR_COURSES_DIR, CACHE_NAME); - PrintWriter writer = null; - try { - if (!createCacheFile(cacheFile)) return; - Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); - - final Set courseInfos = new HashSet<>(); - courseInfos.addAll(courses); - if (preserveOld) { - courseInfos.addAll(getCoursesFromCache()); - } - - writer = new PrintWriter(cacheFile, "UTF-8"); - try { - for (CourseInfo courseInfo : courseInfos) { - final String json = gson.toJson(courseInfo); - writer.println(json); - } - } - finally { - StudyUtils.closeSilently(writer); - } - } - catch (IOException e) { - LOG.error(e); - } - finally { - StudyUtils.closeSilently(writer); - } - } - - private static boolean createCacheFile(File cacheFile) throws IOException { - if (!OUR_COURSES_DIR.exists()) { - final boolean created = OUR_COURSES_DIR.mkdirs(); - if (!created) { - LOG.error("Cannot flush courses cache. Can't create courses directory"); - return false; - } - } - if (!cacheFile.exists()) { - final boolean created = cacheFile.createNewFile(); - if (!created) { - LOG.error("Cannot flush courses cache. Can't create " + CACHE_NAME + " file"); - return false; - } - } - return true; - } - // Supposed to be called under progress - public List getCourses(boolean force) { - if (OUR_COURSES_DIR.exists() && !force) { - myCourses = getCoursesFromCache(); - } - if (force || myCourses.isEmpty()) { + public List getCourses(boolean force) { + if (force) { myCourses = execCancelable(() -> EduStepicConnector.getCourses(StudySettings.getInstance().getUser())); - flushCache(myCourses); } - if (myCourses.isEmpty() || (myCourses.size() == 1 && myCourses.contains(CourseInfo.INVALID_COURSE))) { - myCourses = Collections.singletonList(getBundledIntro()); + if (myCourses == null || myCourses.isEmpty() || (myCourses.size() == 1 && myCourses.contains(Course.INVALID_COURSE))) { + myCourses = getBundledCourses(); } sortCourses(myCourses); return myCourses; } - public void sortCourses(List result) { + public void sortCourses(List result) { // sort courses so as to have non-adaptive courses in the beginning of the list Collections.sort(result, (c1, c2) -> { - if (mySelectedCourseInfo != null) { - if (mySelectedCourseInfo.equals(c1)) { + if (mySelectedCourse != null) { + if (mySelectedCourse.equals(c1)) { return -1; } - if (mySelectedCourseInfo.equals(c2)) { + if (mySelectedCourse.equals(c2)) { return 1; } } @@ -434,7 +144,7 @@ public class StudyProjectGenerator { } @NotNull - public List getCoursesUnderProgress(boolean force, @NotNull final String progressTitle, @NotNull final Project project) { + public List getCoursesUnderProgress(boolean force, @NotNull final String progressTitle, @NotNull final Project project) { try { return ProgressManager.getInstance() .runProcessWithProgressSynchronously(() -> { @@ -443,7 +153,7 @@ public class StudyProjectGenerator { }, progressTitle, true, project); } catch (RuntimeException e) { - return Collections.singletonList(CourseInfo.INVALID_COURSE); + return Collections.singletonList(Course.INVALID_COURSE); } } @@ -461,78 +171,41 @@ public class StudyProjectGenerator { } } - public static CourseInfo getBundledIntro() { - final File introCourse = new File(OUR_COURSES_DIR, "Introduction to Python"); - if (introCourse.exists()) { - return getCourseInfo(introCourse); - } - return null; - } - - public static List getCoursesFromCache() { - List courses = new ArrayList<>(); - final File cacheFile = new File(OUR_COURSES_DIR, CACHE_NAME); - if (!cacheFile.exists()) { - return courses; - } - try { - final FileInputStream inputStream = new FileInputStream(cacheFile); - try { - BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); - try { - String line; - while ((line = reader.readLine()) != null) { - Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); - final CourseInfo courseInfo = gson.fromJson(line, CourseInfo.class); - courses.add(courseInfo); - } - } - catch (IOException | JsonSyntaxException e) { - LOG.error(e.getMessage()); - } - finally { - StudyUtils.closeSilently(reader); - } + @Nullable + public List getBundledCourses() { + final ArrayList courses = new ArrayList<>(); + final LanguageExtensionPoint[] extensions = Extensions.getExtensions(EduPluginConfigurator.EP_NAME, null); + for (LanguageExtensionPoint extension : extensions) { + final EduPluginConfigurator configurator = extension.getInstance(); + final List paths = configurator.getBundledCoursePaths(); + for (String path : paths) { + courses.add(getCourse(path)); } - catch (UnsupportedEncodingException e) { - LOG.error(e.getMessage()); - } - finally { - StudyUtils.closeSilently(inputStream); - } - } - catch (FileNotFoundException e) { - LOG.error(e.getMessage()); } return courses; } - /** - * Adds course from zip archive to courses - * - * @return added course name or null if course is invalid - */ @Nullable - public CourseInfo addLocalCourse(String zipFilePath) { - File file = new File(zipFilePath); + public Course addLocalCourse(String zipFilePath) { + final Course courseInfo = getCourse(zipFilePath); + if (courseInfo != null) { + myCourses.add(0, courseInfo); + } + return courseInfo; + } + + @Nullable + public static Course getCourse(String zipFilePath) { try { - String fileName = file.getName(); - String unzippedName = fileName.substring(0, fileName.indexOf(".")); - File courseDir = new File(OUR_COURSES_DIR, unzippedName); - ZipUtil.unzip(null, courseDir, file, null, null, true); - CourseInfo courseName = addCourse(myCourses, courseDir); - flushCache(myCourses); - if (courseName != null && !courseName.getName().equals(unzippedName)) { - //noinspection ResultOfMethodCallIgnored - File dest = new File(OUR_COURSES_DIR, courseName.getName()); - if (dest.exists()) { - FileUtil.delete(dest); - } - courseDir.renameTo(dest); - //noinspection ResultOfMethodCallIgnored - courseDir.delete(); - } - return courseName; + final JBZipFile zipFile = new JBZipFile(zipFilePath); + final JBZipEntry entry = zipFile.getEntry(EduNames.COURSE_META_FILE); + byte[] bytes = entry.getData(); + final String jsonText = new String(bytes, CharsetToolkit.UTF8_CHARSET); + Gson gson = new GsonBuilder() + .registerTypeAdapter(Task.class, new StudySerializationUtils.Json.TaskAdapter()) + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .create(); + return gson.fromJson(jsonText, Course.class); } catch (IOException e) { LOG.error("Failed to unzip course archive"); @@ -540,80 +213,4 @@ public class StudyProjectGenerator { } return null; } - - /** - * Adds course to courses specified in params - * - * @param courses - * @param courseDir must be directory containing course file - * @return added course name or null if course is invalid - */ - @Nullable - private static CourseInfo addCourse(List courses, File courseDir) { - if (courseDir.isDirectory()) { - File[] courseFiles = courseDir.listFiles((dir, name) -> name.equals(EduNames.COURSE_META_FILE)); - if (courseFiles == null || courseFiles.length != 1) { - LOG.info("User tried to add course with more than one or without course files"); - return null; - } - File courseFile = courseFiles[0]; - CourseInfo courseInfo = getCourseInfo(courseFile); - if (courseInfo != null) { - courses.add(0, courseInfo); - } - return courseInfo; - } - return null; - } - - /** - * Parses course json meta file and finds course name - * - * @return information about course or null if course file is invalid - */ - @Nullable - private static CourseInfo getCourseInfo(File courseFile) { - if (courseFile.isDirectory()) { - File[] courseFiles = courseFile.listFiles((dir, name) -> name.equals(EduNames.COURSE_META_FILE)); - if (courseFiles == null || courseFiles.length != 1) { - LOG.info("More than one or without course files"); - return null; - } - courseFile = courseFiles[0]; - } - CourseInfo courseInfo = null; - BufferedReader reader = null; - try { - if (courseFile.getName().equals(EduNames.COURSE_META_FILE)) { - reader = new BufferedReader(new InputStreamReader(new FileInputStream(courseFile), "UTF-8")); - JsonReader r = new JsonReader(reader); - JsonParser parser = new JsonParser(); - JsonElement el = parser.parse(r); - String courseName = el.getAsJsonObject().get(COURSE_NAME_ATTRIBUTE).getAsString(); - String courseDescription = el.getAsJsonObject().get(COURSE_DESCRIPTION).getAsString(); - JsonArray courseAuthors = el.getAsJsonObject().get(AUTHOR_ATTRIBUTE).getAsJsonArray(); - String language = el.getAsJsonObject().get(LANGUAGE_ATTRIBUTE).getAsString(); - courseInfo = new CourseInfo(); - courseInfo.setName(courseName); - courseInfo.setDescription(courseDescription); - courseInfo.setType("pycharm " + language); - final ArrayList authors = new ArrayList<>(); - for (JsonElement author : courseAuthors) { - final JsonObject authorAsJsonObject = author.getAsJsonObject(); - final StepicUser stepicUser = StepicUser.createEmptyUser(); - stepicUser.setFirstName(authorAsJsonObject.get("first_name").getAsString()); - stepicUser.setLastName(authorAsJsonObject.get("last_name").getAsString()); - authors.add(stepicUser); - } - courseInfo.setAuthors(authors); - } - } - catch (Exception e) { - //error will be shown in UI - } - finally { - StudyUtils.closeSilently(reader); - } - return courseInfo; - } -} +} \ No newline at end of file diff --git a/python/educational-core/src/com/jetbrains/edu/learning/intellij/generation/EduProjectGenerator.java b/python/educational-core/src/com/jetbrains/edu/learning/intellij/generation/EduProjectGenerator.java index fe98b766ea7a..6cac20d04ace 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/intellij/generation/EduProjectGenerator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/intellij/generation/EduProjectGenerator.java @@ -8,8 +8,6 @@ import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; import org.jetbrains.annotations.NotNull; -import java.io.File; - public class EduProjectGenerator extends StudyProjectGenerator { private static final Logger LOG = Logger.getInstance(EduProjectGenerator.class); @@ -21,6 +19,5 @@ public class EduProjectGenerator extends StudyProjectGenerator { return; } StudyTaskManager.getInstance(project).setCourse(course); - course.setCourseDirectory(new File(OUR_COURSES_DIR, mySelectedCourseInfo.getName()).getAbsolutePath()); } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/intellij/localCourses/EduCustomCourseModuleBuilder.java b/python/educational-core/src/com/jetbrains/edu/learning/intellij/localCourses/EduCustomCourseModuleBuilder.java index c630d13e910b..c6020344e10e 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/intellij/localCourses/EduCustomCourseModuleBuilder.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/intellij/localCourses/EduCustomCourseModuleBuilder.java @@ -14,7 +14,6 @@ import com.intellij.openapi.util.InvalidDataException; import com.jetbrains.edu.learning.EduPluginConfigurator; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.CourseInfo; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; import com.jetbrains.edu.learning.intellij.generation.EduProjectGenerator; import org.jdom.JDOMException; @@ -27,7 +26,7 @@ import java.io.IOException; class EduCustomCourseModuleBuilder extends JavaModuleBuilder { private static final Logger LOG = Logger.getInstance(EduCustomCourseModuleBuilder.class); private EduProjectGenerator myGenerator = new EduProjectGenerator(); - private CourseInfo mySelectedCourse; + private Course mySelectedCourse; @Nullable @Override @@ -36,8 +35,7 @@ class EduCustomCourseModuleBuilder extends JavaModuleBuilder { if (module == null) { return null; } - String type = mySelectedCourse.getType(); - String languageName = type.substring("pycharm ".length()); + String languageName = mySelectedCourse.getLanguageID(); Language language = Language.findLanguageByID(languageName); if (language == null) { return module; @@ -87,7 +85,7 @@ class EduCustomCourseModuleBuilder extends JavaModuleBuilder { return ProjectWizardStepFactory.getInstance().createJavaSettingsStep(settingsStep, this, Conditions.alwaysTrue()); } - void setSelectedCourse(CourseInfo selectedCourse) { + void setSelectedCourse(Course selectedCourse) { mySelectedCourse = selectedCourse; } @@ -97,8 +95,7 @@ class EduCustomCourseModuleBuilder extends JavaModuleBuilder { throws InvalidDataException, IOException, ModuleWithNameAlreadyExists, JDOMException, ConfigurationException { Module baseModule = super.createModule(moduleModel); if (mySelectedCourse != null) { - String type = mySelectedCourse.getType(); - String languageName = type.substring("pycharm ".length()); + String languageName = mySelectedCourse.getLanguageID(); Language language = Language.findLanguageByID(languageName); if (language != null) { EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(language); diff --git a/python/educational-core/src/com/jetbrains/edu/learning/intellij/localCourses/EduLocalCoursePanel.java b/python/educational-core/src/com/jetbrains/edu/learning/intellij/localCourses/EduLocalCoursePanel.java index d98e6fee5493..790b2829865a 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/intellij/localCourses/EduLocalCoursePanel.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/intellij/localCourses/EduLocalCoursePanel.java @@ -2,8 +2,8 @@ package com.jetbrains.edu.learning.intellij.localCourses; import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; -import com.jetbrains.edu.learning.courseFormat.CourseInfo; import com.jetbrains.edu.learning.ui.StudyNewProjectPanel; import javax.swing.*; @@ -25,7 +25,7 @@ public class EduLocalCoursePanel { @Override public void actionPerformed(ActionEvent actionEvent) { String coursePath = myCourseArchivePath.getText(); - CourseInfo courseInfo = generator.addLocalCourse(coursePath); + Course courseInfo = generator.addLocalCourse(coursePath); if (courseInfo != null) { myBuilder.setSelectedCourse(courseInfo); myGenerator.setSelectedCourse(courseInfo); @@ -45,7 +45,7 @@ public class EduLocalCoursePanel { } private void createUIComponents() { - myStudyPanel = new StudyNewProjectPanel(myGenerator); + myStudyPanel = new StudyNewProjectPanel(myGenerator, true); myInfoPanel = myStudyPanel.getInfoPanel(); } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java b/python/educational-core/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java index 2da4ed9f1394..85b709a9dc63 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java @@ -41,7 +41,15 @@ public class StudyNavigator { if (nextLesson == null) { return null; } - return StudyUtils.getFirst(nextLesson.getTaskList()); + List nextLessonTaskList = nextLesson.getTaskList(); + while (nextLessonTaskList.isEmpty()) { + nextLesson = nextLesson(nextLesson); + if (nextLesson == null) { + return null; + } + nextLessonTaskList = nextLesson.getTaskList(); + } + return StudyUtils.getFirst(nextLessonTaskList); } public static Task previousTask(@NotNull final Task task) { @@ -55,7 +63,15 @@ public class StudyNavigator { return null; } //getting last task in previous lesson - return prevLesson.getTaskList().get(prevLesson.getTaskList().size() - 1); + List prevLessonTaskList = prevLesson.getTaskList(); + while (prevLessonTaskList.isEmpty()) { + prevLesson = previousLesson(prevLesson); + if (prevLesson == null) { + return null; + } + prevLessonTaskList = prevLesson.getTaskList(); + } + return prevLessonTaskList.get(prevLessonTaskList.size() - 1); } public static Lesson nextLesson(@NotNull final Lesson lesson) { @@ -64,11 +80,7 @@ public class StudyNavigator { if (nextLessonIndex >= lessons.size()) { return null; } - final Lesson nextLesson = lessons.get(nextLessonIndex); - if (EduNames.PYCHARM_ADDITIONAL.equals(nextLesson.getName())) { - return null; - } - return nextLesson; + return lessons.get(nextLessonIndex); } public static Lesson previousLesson(@NotNull final Lesson lesson) { diff --git a/python/educational-core/src/com/jetbrains/edu/learning/statistics/EduUsagesCollector.java b/python/educational-core/src/com/jetbrains/edu/learning/statistics/EduUsagesCollector.java index f500a1ee179e..c4db5d3caf2b 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/statistics/EduUsagesCollector.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/statistics/EduUsagesCollector.java @@ -23,7 +23,6 @@ import com.intellij.openapi.components.ServiceManager; import com.intellij.util.containers.FactoryMap; import com.intellij.util.containers.hash.HashSet; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.Set; @@ -31,7 +30,6 @@ public class EduUsagesCollector extends UsagesCollector { private static final String GROUP_ID = "educational"; private final FactoryMap myUsageDescriptors = new FactoryMap() { - @Nullable @Override protected UsageDescriptor create(String key) { return new UsageDescriptor(key, 0); diff --git a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java index 2938f63bd0bc..3c0f635fce8f 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java @@ -7,15 +7,12 @@ import com.intellij.ide.projectView.ProjectView; import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.ui.MessageType; -import com.intellij.openapi.ui.popup.Balloon; -import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -31,7 +28,6 @@ import com.jetbrains.edu.learning.courseFormat.tasks.CodeTask; import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.courseFormat.tasks.TheoryTask; import com.jetbrains.edu.learning.courseGeneration.StudyGenerator; -import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.navigation.StudyNavigator; import com.jetbrains.edu.learning.ui.StudyToolWindow; @@ -49,15 +45,11 @@ import org.apache.http.util.EntityUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; +import java.util.*; import java.util.concurrent.TimeUnit; import static com.jetbrains.edu.learning.stepic.EduStepicConnector.getStep; @@ -72,11 +64,12 @@ public class EduAdaptiveStepicConnector { private static final Logger LOG = Logger.getInstance(EduAdaptiveStepicConnector.class); private static final int CONNECTION_TIMEOUT = 60 * 1000; private static final String CODE_TASK_TYPE = "code"; + private static final String CODE_TASK_NAME = "code.py"; private static final String CHOICE_TYPE_TEXT = "choice"; private static final String TEXT_STEP_TYPE = "text"; @Nullable - public static Task getNextRecommendation(@NotNull Project project, @NotNull Course course) { + public static Task getNextRecommendation(@NotNull Project project, @NotNull RemoteCourse course) { try { final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); if (client == null) { @@ -134,16 +127,8 @@ public class EduAdaptiveStepicConnector { } catch (IOException e) { LOG.warn(e.getMessage()); - - final String connectionMessages = "Connection problems, Please, try again"; - final Balloon balloon = - JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(connectionMessages, MessageType.ERROR, null) - .createBalloon(); - ApplicationManager.getApplication().invokeLater(() -> { - if (StudyUtils.getSelectedEditor(project) != null) { - StudyUtils.showCheckPopUp(project, balloon); - } - }); + ApplicationManager.getApplication() + .invokeLater(() -> StudyUtils.showErrorPopupOnToolbar(project, "Connection problems, Please, try again")); } catch (URISyntaxException e) { LOG.warn(e.getMessage()); @@ -209,8 +194,8 @@ public class EduAdaptiveStepicConnector { private static void createMockTaskFile(@NotNull Task task, String editorText) { final TaskFile taskFile = new TaskFile(); taskFile.text = editorText; - taskFile.name = "code"; - task.taskFiles.put("code.py", taskFile); + taskFile.name = CODE_TASK_NAME; + task.taskFiles.put(CODE_TASK_NAME, taskFile); } @Nullable @@ -267,9 +252,7 @@ public class EduAdaptiveStepicConnector { request.setConfig(requestConfig); } - public static boolean postRecommendationReaction(@NotNull String lessonId, - @NotNull String user, - int reaction) { + public static boolean postRecommendationReaction(@NotNull String lessonId, @NotNull String user, int reaction) { final HttpPost post = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.RECOMMENDATION_REACTIONS_URL); final String json = new Gson() .toJson(new StepicWrappers.RecommendationReactionWrapper(new StepicWrappers.RecommendationReaction(reaction, user, lessonId))); @@ -293,133 +276,171 @@ public class EduAdaptiveStepicConnector { } catch (IOException e) { LOG.warn(e.getMessage()); + return false; } - return false; } - public static void addNextRecommendedTask(@NotNull Project project, - @NotNull ProgressIndicator indicator, - int reaction) { + public static void addNextRecommendedTask(@NotNull Project project, @NotNull ProgressIndicator indicator, int reactionToPost) { final StudyEditor editor = StudyUtils.getSelectedStudyEditor(project); final Course course = StudyTaskManager.getInstance(project).getCourse(); - if (course != null && editor != null && editor.getTaskFile() != null) { - indicator.checkCanceled(); - final StepicUser user = StudySettings.getInstance().getUser(); - if (user == null) { - LOG.warn("Can't get next recommendation: user is null"); - ApplicationManager.getApplication().invokeLater(() -> StudyUtils.showErrorPopupOnToolbar(project, - "Can't get next recommendation: you're not authorized")); - return; - } - final boolean recommendationReaction = postRecommendationReaction(String.valueOf(editor.getTaskFile().getTask().getLesson().getId()), - String.valueOf(user.getId()), reaction); - if (recommendationReaction) { - indicator.checkCanceled(); - final Task task = getNextRecommendation(project, course); + if (course == null || editor == null || editor.getTaskFile() == null || !(course instanceof RemoteCourse)) { + return; + } - if (task != null) { - final Lesson adaptive = course.getLessons().get(0); - task.initTask(adaptive, false); - final Task unsolvedTask = adaptive.getTaskList().get(adaptive.getTaskList().size() - 1); - final String lessonName = EduNames.LESSON + String.valueOf(adaptive.getIndex()); - if (reaction == TOO_HARD_RECOMMENDATION_REACTION || reaction == TOO_BORING_RECOMMENDATION_REACTION) { - task.setLesson(unsolvedTask.getLesson()); - task.setIndex(unsolvedTask.getIndex()); - adaptive.getTaskList().set(adaptive.getTaskList().size() - 1, task); - ApplicationManager.getApplication().invokeLater(()->StudyNavigator.navigateToTask(project, task)); + indicator.checkCanceled(); + final StepicUser user = StudySettings.getInstance().getUser(); + if (user == null) { + LOG.warn("Can't get next recommendation: user is null"); + ApplicationManager.getApplication().invokeLater(() -> StudyUtils.showErrorPopupOnToolbar(project, + "Can't get next recommendation: you're not authorized")); + return; + } - final File lessonDirectory = new File(course.getCourseDirectory(), lessonName); - final String taskName = EduNames.TASK + String.valueOf(adaptive.getTaskList().size()); - final File taskDirectory = new File(lessonDirectory, taskName); - StudyProjectGenerator.flushTask(task, taskDirectory); - StudyProjectGenerator.flushCourseJson(course, new File(course.getCourseDirectory())); - final VirtualFile lessonDir = project.getBaseDir().findChild(lessonName); + final Lesson lesson = editor.getTaskFile().getTask().getLesson(); + final boolean reactionPosted = postRecommendationReaction(String.valueOf(lesson.getId()), String.valueOf(user.getId()), reactionToPost); + if (!reactionPosted) { + LOG.warn("Recommendation reaction wasn't posted"); + ApplicationManager.getApplication().invokeLater(() -> StudyUtils.showErrorPopupOnToolbar(project, "Couldn't post your reactionToPost")); + return; + } - if (lessonDir != null) { - createTestFiles(course, project, unsolvedTask, lessonDir); - } - final StudyToolWindow window = StudyUtils.getStudyToolWindow(project); - if (window != null) { - window.setTaskText(StudyUtils.wrapTextToDisplayLatex(unsolvedTask.getTaskDescription()), unsolvedTask.getTaskDir(project), project); - } - StudyNavigator.navigateToTask(project, lessonName, taskName); - } - else { - adaptive.addTask(task); - task.setIndex(adaptive.getTaskList().size()); - final VirtualFile lessonDir = project.getBaseDir().findChild(lessonName); + indicator.checkCanceled(); + final Task task = getNextRecommendation(project, (RemoteCourse)course); + if (task == null) { + ApplicationManager.getApplication().invokeLater(() -> StudyUtils.showErrorPopupOnToolbar(project, + "Couldn't load a new recommendation")); + return; + } - if (lessonDir != null) { - ApplicationManager.getApplication().invokeLater(() -> ApplicationManager.getApplication().runWriteAction(() -> { - try { - final File lessonDirectory = new File(course.getCourseDirectory(), lessonName); - final String taskName = EduNames.TASK + String.valueOf(task.getIndex()); - final File taskDir = new File(lessonDirectory, taskName); - StudyProjectGenerator.flushTask(task, taskDir); - StudyProjectGenerator.flushCourseJson(course, new File(course.getCourseDirectory())); - StudyGenerator.createTask(task, lessonDir, new File(course.getCourseDirectory(), lessonDir.getName()), project); - adaptive.initLesson(course, true); - StudyNavigator.navigateToTask(project, lessonName, taskName); - } - catch (IOException e) { - LOG.warn(e.getMessage()); - } - })); - } - } + task.initTask(lesson, false); + boolean replaceCurrentTask = reactionToPost == TOO_HARD_RECOMMENDATION_REACTION || reactionToPost == TOO_BORING_RECOMMENDATION_REACTION; + if (replaceCurrentTask) { + replaceCurrentTask(project, editor, task); + } + else { + addAsNextTask(project, editor, task); + } + + ApplicationManager.getApplication().invokeLater(() -> { + VirtualFileManager.getInstance().refreshWithoutFileWatcher(false); + ProjectView.getInstance(project).refresh(); + StudyNavigator.navigateToTask(project, task); + }); + } + + private static void addAsNextTask(@NotNull Project project, @NotNull StudyEditor editor, @NotNull Task task) { + Course course = StudyTaskManager.getInstance(project).getCourse(); + assert course != null; + + final Lesson lesson = editor.getTaskFile().getTask().getLesson(); + lesson.addTask(task); + task.setIndex(lesson.getTaskList().size()); + lesson.initLesson(course, true); + + final String lessonName = EduNames.LESSON + lesson.getIndex(); + createFilesForNewTask(project, task, lessonName); + } + + private static void createFilesForNewTask(@NotNull Project project, + @NotNull Task task, + @NotNull String lessonName) { + final VirtualFile lessonDir = project.getBaseDir().findChild(lessonName); + if (lessonDir != null) { + ApplicationManager.getApplication().invokeLater(() -> ApplicationManager.getApplication().runWriteAction(() -> { + try { + StudyGenerator.createTask(task, lessonDir); } - else { - ApplicationManager.getApplication().invokeLater(() -> { - final Balloon balloon = - JBPopupFactory.getInstance().createHtmlTextBalloonBuilder("Couldn't load a new recommendation", MessageType.ERROR, null) - .createBalloon(); - StudyUtils.showCheckPopUp(project, balloon); - }); + catch (IOException e) { + LOG.warn(e.getMessage()); } - ApplicationManager.getApplication().invokeLater(() -> { - VirtualFileManager.getInstance().refreshWithoutFileWatcher(false); - ProjectView.getInstance(project).refresh(); - }); - } - else { - LOG.warn("Recommendation reactions weren't posted"); - ApplicationManager.getApplication().invokeLater(() -> StudyUtils.showErrorPopupOnToolbar(project, "Couldn't post your reaction")); + })); + } + } + + private static void replaceCurrentTask(@NotNull Project project, @NotNull StudyEditor editor, @NotNull Task task) { + Course course = StudyTaskManager.getInstance(project).getCourse(); + assert course != null; + + final Lesson lesson = editor.getTaskFile().getTask().getLesson(); + int taskIndex = lesson.getTaskList().size(); + + task.setLesson(lesson); + task.setIndex(taskIndex); + lesson.getTaskList().set(taskIndex - 1, task); + + copyTaskFileParameters(editor, task); + + final String lessonName = EduNames.LESSON + lesson.getIndex(); + updateProjectFiles(project, task, lessonName); + setToolWindowText(project, task); + } + + private static void updateProjectFiles(@NotNull Project project, @NotNull Task task, @NotNull String lessonName) { + final VirtualFile lessonDir = project.getBaseDir().findChild(lessonName); + if (lessonDir != null) { + final VirtualFile taskResourceRoot = lessonDir.findChild(EduNames.TASK + task.getIndex()); + ApplicationManager.getApplication().invokeLater(() -> ApplicationManager.getApplication().runWriteAction(() -> { + try { + removeOldProjectFiles(project, taskResourceRoot); + StudyGenerator.createTestFiles(taskResourceRoot, task); + StudyGenerator.createDescriptions(taskResourceRoot, task); + } + catch (IOException e) { + LOG.warn(e.getMessage()); + } + })); + } + } + + private static void removeOldProjectFiles(@NotNull Project project, @Nullable VirtualFile taskRecourseRoot) throws IOException { + if (taskRecourseRoot == null) { + LOG.warn("Failed to update files for a new recommendation: task directory is null"); + return; + } + + VirtualFile[] children = VfsUtil.getChildren(taskRecourseRoot); + for (VirtualFile file : children) { + String name = file.getName(); + if (StudyUtils.isTestsFile(project, name) || StudyUtils.isTaskDescriptionFile(name)) { + file.delete(taskRecourseRoot); } } } - private static void createTestFiles(@NotNull Course course, @NotNull Project project, - @NotNull Task unsolvedTask, @NotNull VirtualFile lessonDir) { + private static void copyTaskFileParameters(@NotNull StudyEditor editor, @NotNull Task task) { + final Map taskFiles = task.getTaskFiles(); + if (taskFiles.size() == 1) { + TaskFile newTaskFile = (TaskFile)taskFiles.values().toArray()[0]; + setTaskFileParameters(editor, task, newTaskFile); + updateEditorText(editor, newTaskFile); + } + else { + LOG.warn("Got task without unexpected number of task files: " + taskFiles.size()); + } + } + private static void updateEditorText(@NotNull StudyEditor editor, @NotNull TaskFile newTaskFile) { ApplicationManager.getApplication().invokeLater(() -> ApplicationManager.getApplication().runWriteAction(() -> { - try { - final VirtualFile taskDir = VfsUtil - .findFileByIoFile(new File(lessonDir.getCanonicalPath(), EduNames.TASK + unsolvedTask.getIndex()), true); - final File resourceRoot = new File(course.getCourseDirectory(), lessonDir.getName()); - - if (taskDir != null) { - final File newResourceRoot = new File(resourceRoot, taskDir.getName()); - File[] filesInTask = newResourceRoot.listFiles(); - if (filesInTask != null) { - for (File file : filesInTask) { - final String taskRelativePath = FileUtil.getRelativePath(taskDir.getPath(), file.getPath(), '/'); - if (taskRelativePath != null && StudyUtils.isTestsFile(project, taskDir.getName())) { - final File resourceFile = new File(newResourceRoot, taskRelativePath); - final File fileInProject = new File(taskDir.getCanonicalPath(), taskRelativePath); - FileUtil.copy(resourceFile, fileInProject); - } - } - } - } - else { - LOG.warn("Task directory is null"); - } - } - catch (IOException e) { - LOG.warn(e.getMessage()); - } + final Document document = editor.getEditor().getDocument(); + document.setText(newTaskFile.text); })); } + private static void setTaskFileParameters(@NotNull StudyEditor editor, @NotNull Task task, @NotNull TaskFile newTaskFile) { + TaskFile currentTaskFile = editor.getTaskFile(); + currentTaskFile.text = newTaskFile.text; + currentTaskFile.name = newTaskFile.name; + currentTaskFile.setTask(task); + task.getTaskFiles().clear(); + task.taskFiles.put(CODE_TASK_NAME, currentTaskFile); + } + + private static void setToolWindowText(@NotNull Project project, @NotNull Task task) { + final StudyToolWindow window = StudyUtils.getStudyToolWindow(project); + if (window != null) { + window.setTaskText(StudyUtils.wrapTextToDisplayLatex(task.getTaskDescription()), task.getTaskDir(project), project); + } + } + @NotNull private static Task getCodeTaskFromStep(@NotNull Project project, @NotNull StepicWrappers.Step step, @@ -468,11 +489,9 @@ public class EduAdaptiveStepicConnector { } } else { - final TaskFile taskFile = new TaskFile(); - taskFile.name = CODE_TASK_TYPE; final String templateForTask = getCodeTemplateForTask(project, task, step.options.codeTemplates); - taskFile.text = templateForTask == null ? "# write your answer here \n" : templateForTask; - task.taskFiles.put("code.py", taskFile); + String text = templateForTask == null ? "# write your answer here \n" : templateForTask; + createMockTaskFile(task, text); } return task; } @@ -533,7 +552,6 @@ public class EduAdaptiveStepicConnector { return answer; } - @Nullable public static Pair checkCodeTask(@NotNull Project project, @NotNull Task task, @NotNull StepicUser user) { int attemptId = -1; try { @@ -693,11 +711,11 @@ public class EduAdaptiveStepicConnector { public static List getEnrolledCoursesIds(@NotNull StepicUser stepicUser) { try { final URI enrolledCoursesUri = new URIBuilder(EduStepicNames.COURSES).addParameter("enrolled", "true").build(); - final List courses = EduStepicAuthorizedClient.getFromStepic(enrolledCoursesUri.toString(), + final List courses = EduStepicAuthorizedClient.getFromStepic(enrolledCoursesUri.toString(), StepicWrappers.CoursesContainer.class, stepicUser).courses; final ArrayList ids = new ArrayList<>(); - for (CourseInfo course : courses) { + for (RemoteCourse course : courses) { ids.add(course.getId()); } return ids; diff --git a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicClient.java b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicClient.java index d81b6f8bd53c..d4ed1b144f15 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicClient.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicClient.java @@ -25,6 +25,7 @@ import org.jetbrains.annotations.NotNull; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import java.io.IOException; +import java.lang.reflect.Modifier; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.URI; @@ -70,6 +71,7 @@ public class EduStepicClient { static T deserializeStepicResponse(Class container, String responseString) { Gson gson = new GsonBuilder().registerTypeAdapter(StepicWrappers.StepOptions.class, new StudySerializationUtils.Json.StepicStepOptionsAdapter()). + excludeFieldsWithModifiers(Modifier.TRANSIENT). setDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"). setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); return gson.fromJson(responseString, container); diff --git a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java index 1931f7a2e768..fb0998541a16 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java @@ -11,10 +11,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.jetbrains.edu.learning.StudySettings; import com.jetbrains.edu.learning.core.EduNames; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.CourseInfo; -import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.courseFormat.*; import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks; import org.apache.http.HttpEntity; @@ -65,9 +62,9 @@ public class EduStepicConnector { } @NotNull - public static List getCourses(@Nullable StepicUser user) { + public static List getCourses(@Nullable StepicUser user) { try { - List result = new ArrayList<>(); + List result = new ArrayList<>(); int pageNumber = 1; while (addCoursesFromStepic(user, result, pageNumber)) { pageNumber += 1; @@ -77,13 +74,13 @@ public class EduStepicConnector { catch (IOException e) { LOG.warn("Cannot load course list " + e.getMessage()); } - return Collections.singletonList(CourseInfo.INVALID_COURSE); + return Collections.emptyList(); } public static Date getCourseUpdateDate(final int courseId) { final String url = EduStepicNames.COURSES + "/" + courseId; try { - final List courses = EduStepicClient.getFromStepic(url, StepicWrappers.CoursesContainer.class).courses; + final List courses = EduStepicClient.getFromStepic(url, StepicWrappers.CoursesContainer.class).courses; if (!courses.isEmpty()) { return courses.get(0).getUpdateDate(); } @@ -125,7 +122,7 @@ public class EduStepicConnector { return null; } - private static boolean addCoursesFromStepic(@Nullable StepicUser user, List result, int pageNumber) throws IOException { + private static boolean addCoursesFromStepic(@Nullable StepicUser user, List result, int pageNumber) throws IOException { final URI url; try { url = new URIBuilder(EduStepicNames.COURSES).addParameter("is_idea_compatible", "true"). @@ -146,27 +143,28 @@ public class EduStepicConnector { return coursesContainer.meta.containsKey("has_next") && coursesContainer.meta.get("has_next") == Boolean.TRUE; } - static void addAvailableCourses(List result, StepicWrappers.CoursesContainer coursesContainer) throws IOException { - final List courseInfos = coursesContainer.courses; - for (CourseInfo info : courseInfos) { + static void addAvailableCourses(List result, StepicWrappers.CoursesContainer coursesContainer) throws IOException { + final List courses = coursesContainer.courses; + for (RemoteCourse info : courses) { if (!info.isAdaptive() && StringUtil.isEmptyOrSpaces(info.getType())) continue; if (canBeOpened(info)) { + final ArrayList authors = new ArrayList<>(); for (Integer instructor : info.getInstructors()) { final StepicUser author = EduStepicClient.getFromStepic(EduStepicNames.USERS + String.valueOf(instructor), StepicWrappers.AuthorWrapper.class).users.get(0); - info.addAuthor(author); + authors.add(author); } + info.setAuthors(authors); if (info.isAdaptive()) { info.setDescription("This is a Stepik Adaptive course.\n\n" + info.getDescription() + ADAPTIVE_NOTE); } - result.add(info); } } } - static boolean canBeOpened(CourseInfo courseInfo) { + static boolean canBeOpened(RemoteCourse courseInfo) { if (courseInfo.isAdaptive()) { return true; } @@ -190,26 +188,19 @@ public class EduStepicConnector { } } - public static Course getCourse(@NotNull final Project project, @NotNull final CourseInfo info) { - final Course course = new Course(); - course.setAuthors(info.getAuthors()); - course.setDescription(info.getDescription()); - course.setAdaptive(info.isAdaptive()); - course.setId(info.getId()); - course.setUpdateDate(getCourseUpdateDate(info.getId())); - - if (!course.isAdaptive()) { - String courseType = info.getType(); - course.setName(info.getName()); + public static RemoteCourse getCourse(@NotNull final Project project, @NotNull final RemoteCourse course) { + final RemoteCourse remoteCourse = (RemoteCourse)course.copy(); + if (!remoteCourse.isAdaptive()) { + String courseType = remoteCourse.getType(); final int separator = courseType.indexOf(" "); assert separator != -1; final String language = courseType.substring(separator + 1); - course.setLanguage(language); + remoteCourse.setLanguage(language); try { - for (Integer section : info.getSections()) { - course.addLessons(getLessons(section)); + for (Integer section : remoteCourse.getSections()) { + remoteCourse.addLessons(getLessons(section)); } - return course; + return remoteCourse; } catch (IOException e) { LOG.error("IOException " + e.getMessage()); @@ -218,14 +209,13 @@ public class EduStepicConnector { else { final Lesson lesson = new Lesson(); lesson.setName("Adaptive"); - course.addLesson(lesson); - course.setName(info.getName()); + remoteCourse.addLesson(lesson); //TODO: more specific name? - final Task recommendation = EduAdaptiveStepicConnector.getNextRecommendation(project, course); + final Task recommendation = EduAdaptiveStepicConnector.getNextRecommendation(project, remoteCourse); if (recommendation != null) { lesson.addTask(recommendation); } - return course; + return remoteCourse; } return null; } @@ -299,12 +289,26 @@ public class EduStepicConnector { task.taskFiles = new HashMap<>(); // TODO: it looks like we don't need taskFiles as map anymore if (block.options.files != null) { for (TaskFile taskFile : block.options.files) { + addPlaceholdersTexts(taskFile); task.taskFiles.put(taskFile.name, taskFile); } } return task; } + private static void addPlaceholdersTexts(TaskFile file) { + final String fileText = file.text; + final List placeholders = file.getAnswerPlaceholders(); + for (AnswerPlaceholder placeholder : placeholders) { + final AnswerPlaceholderSubtaskInfo info = placeholder.getActiveSubtaskInfo(); + final int offset = placeholder.getOffset(); + final int length = placeholder.getLength(); + if (fileText.length() > offset + length) { + info.setPlaceholderText(fileText.substring(offset, offset+length)); + } + } + } + @NotNull private static Task createTaskWithSubtasks(int lastSubtaskIndex) { TaskWithSubtasks task = new TaskWithSubtasks(); diff --git a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicUpdater.java b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicUpdater.java index ed8afb093f92..ac5c4b1a438a 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicUpdater.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicUpdater.java @@ -11,10 +11,12 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.Alarm; import com.intellij.util.text.DateFormatUtil; import com.jetbrains.edu.learning.StudySettings; -import com.jetbrains.edu.learning.courseFormat.CourseInfo; -import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.RemoteCourse; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.Date; import java.util.List; public class EduStepicUpdater { @@ -49,25 +51,29 @@ public class EduStepicUpdater { private static ActionCallback updateCourseList() { ActionCallback callback = new ActionCallback(); ApplicationManager.getApplication().executeOnPooledThread(() -> { - final List courses = EduStepicConnector.getCourses(null); - final List cachedCourses = StudyProjectGenerator.getCoursesFromCache(); - StudyProjectGenerator.flushCache(courses); + final List courses = EduStepicConnector.getCourses(null); StudySettings.getInstance().setLastTimeChecked(System.currentTimeMillis()); - courses.removeAll(cachedCourses); - if (!courses.isEmpty() && !cachedCourses.isEmpty()) { + if (!courses.isEmpty()) { + List updated = new ArrayList<>(); + for (Course course : courses) { + if (course instanceof RemoteCourse && ((RemoteCourse)course).getUpdateDate(). + after(new Date(StudySettings.getInstance().getLastTimeChecked()))) { + updated.add(course); + } + } + if (updated.isEmpty()) return; final String message; final String title; - if (courses.size() == 1) { - message = courses.get(0).getName(); + if (updated.size() == 1) { + message = updated.get(0).getName(); title = "New course available"; } else { title = "New courses available"; - message = StringUtil.join(courses, CourseInfo::getName, ", "); + message = StringUtil.join(updated, Course::getName, ", "); } - final Notification notification = - new Notification("New.course", title, message, NotificationType.INFORMATION); + final Notification notification = new Notification("New.course", title, message, NotificationType.INFORMATION); notification.notify(null); } }); @@ -79,8 +85,7 @@ public class EduStepicUpdater { } private static boolean checkNeeded() { - final List courses = StudyProjectGenerator.getCoursesFromCache(); long timeToNextCheck = StudySettings.getInstance().getLastTimeChecked() + CHECK_INTERVAL - System.currentTimeMillis(); - return courses.isEmpty() || timeToNextCheck <= 0; + return timeToNextCheck <= 0; } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicUser.java b/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicUser.java index 16a043bbe463..743b3ab2b7bf 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicUser.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicUser.java @@ -87,6 +87,10 @@ public class StepicUser { @NotNull public String getAccessToken() { + // for old project where authors were created with null tokens + if (myAccessToken == null) { + return ""; + } return myAccessToken; } @@ -96,6 +100,10 @@ public class StepicUser { @NotNull public String getRefreshToken() { + // for old project where authors were created with null tokens + if (myRefreshToken == null) { + return ""; + } return myRefreshToken; } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java b/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java index b5a2065c3135..1690b383f063 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java @@ -5,25 +5,21 @@ import com.google.gson.annotations.SerializedName; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.CourseInfo; import com.jetbrains.edu.learning.courseFormat.Lesson; +import com.jetbrains.edu.learning.courseFormat.RemoteCourse; import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks; -import org.apache.commons.codec.binary.Base64; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; -import java.io.InputStream; import java.util.*; import java.util.stream.Collectors; @@ -89,21 +85,11 @@ public class StepicWrappers { } String name = entry.getKey(); VirtualFile answerFile = taskDir.findFileByRelativePath(name); - Pair pair = EduUtils.createStudentFile(StepicWrappers.class, project, answerFile, stepicDir, null, 0); - if (pair == null) { + TaskFile studentTaskFile = EduUtils.createStudentFile(project, answerFile, null, 0); + if (studentTaskFile == null) { return; } - VirtualFile virtualFile = pair.getFirst(); - TaskFile taskFile = pair.getSecond(); - try { - InputStream stream = virtualFile.getInputStream(); - taskFile.text = - EduUtils.isImage(name) ? Base64.encodeBase64URLSafeString(FileUtil.loadBytes(stream)) : FileUtil.loadTextAndClose(stream); - } - catch (IOException e) { - LOG.error("Can't find file " + virtualFile.getPath()); - } - source.files.add(taskFile); + source.files.add(studentTaskFile); }); } return source; @@ -185,7 +171,7 @@ public class StepicWrappers { } public static class CoursesContainer { - public List courses; + public List courses; public Map meta; } @@ -199,10 +185,10 @@ public class StepicWrappers { } public static class CourseWrapper { - CourseInfo course; + RemoteCourse course; public CourseWrapper(Course course) { - this.course = new CourseInfo(); + this.course = new RemoteCourse(); this.course.setName(course.getName()); this.course.setDescription(course.getDescription()); this.course.setAuthors(course.getAuthors()); diff --git a/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java b/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java index e236b5487fa3..f7eb72f4b607 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java @@ -29,7 +29,6 @@ import com.intellij.util.ui.UIUtil; import com.jetbrains.edu.learning.StudySettings; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.CourseInfo; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector; import com.jetbrains.edu.learning.stepic.EduStepicAuthorizedClient; @@ -52,15 +51,16 @@ import java.util.List; * data: 7/31/14. */ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor { - private List myAvailableCourses = new ArrayList<>(); + private List myAvailableCourses = new ArrayList<>(); private JButton myBrowseButton; - private ComboBox myCoursesComboBox; + private ComboBox myCoursesComboBox; private JButton myRefreshButton; private JLabel myAuthorLabel; private JPanel myInfoPanel; private JTextPane myDescriptionPane; private JComponent myAnchor; private final StudyProjectGenerator myGenerator; + private boolean isLocal = false; private static final String CONNECTION_ERROR = "Failed to download courses.
Check your Internet connection."; private static final String INVALID_COURSE = "Selected course is invalid"; private FacetValidatorsManager myValidationManager; @@ -68,10 +68,10 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor { private static final String LOGIN_TO_STEPIC_MESSAGE = "Login to Stepik to open the adaptive course "; private static final String LOGIN_TO_STEPIC = "Login to Stepik"; - public StudyNewProjectPanel(@NotNull final StudyProjectGenerator generator) { + public StudyNewProjectPanel(@NotNull final StudyProjectGenerator generator, boolean isLocal) { super(new VerticalFlowLayout(true, true)); myGenerator = generator; - + this.isLocal = isLocal; layoutPanel(); initListeners(); } @@ -120,21 +120,21 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor { private void initCoursesCombobox() { myAvailableCourses = - myGenerator.getCoursesUnderProgress(false, "Getting Available Courses", ProjectManager.getInstance().getDefaultProject()); - if (myAvailableCourses.contains(CourseInfo.INVALID_COURSE)) { + myGenerator.getCoursesUnderProgress(!isLocal, "Getting Available Courses", ProjectManager.getInstance().getDefaultProject()); + if (myAvailableCourses.contains(Course.INVALID_COURSE)) { setError(CONNECTION_ERROR); } else { addCoursesToCombobox(myAvailableCourses); - final CourseInfo selectedCourse = StudyUtils.getFirst(myAvailableCourses); + final Course selectedCourse = StudyUtils.getFirst(myAvailableCourses); if (selectedCourse == null) return; setAuthors(selectedCourse); myDescriptionPane.setText(selectedCourse.getDescription()); myDescriptionPane.setEditable(false); //setting the first course in list as selected myGenerator.setSelectedCourse(selectedCourse); - if (myGenerator.getSelectedCourseInfo() != null) { - myCoursesComboBox.setSelectedItem(myGenerator.getSelectedCourseInfo()); + if (myGenerator.getSelectedCourse() != null) { + myCoursesComboBox.setSelectedItem(myGenerator.getSelectedCourse()); } if (selectedCourse.isAdaptive() && !myGenerator.isLoggedIn()) { setError(LOGIN_TO_STEPIC_MESSAGE); @@ -173,7 +173,7 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor { file -> { String fileName = file.getPath(); int oldSize = myAvailableCourses.size(); - CourseInfo courseInfo = myGenerator.addLocalCourse(fileName); + Course courseInfo = myGenerator.addLocalCourse(fileName); if (courseInfo != null) { if (oldSize != myAvailableCourses.size()) { myCoursesComboBox.addItem(courseInfo); @@ -184,11 +184,11 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor { else { setError(INVALID_COURSE); myCoursesComboBox.removeAllItems(); - myCoursesComboBox.addItem(CourseInfo.INVALID_COURSE); - for (CourseInfo course : myAvailableCourses) { + myCoursesComboBox.addItem(Course.INVALID_COURSE); + for (Course course : myAvailableCourses) { myCoursesComboBox.addItem(course); } - myCoursesComboBox.setSelectedItem(CourseInfo.INVALID_COURSE); + myCoursesComboBox.setSelectedItem(Course.INVALID_COURSE); } }); } @@ -220,8 +220,8 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor { isComboboxInitialized = true; initCoursesCombobox(); } - CourseInfo selectedCourse = (CourseInfo)myCoursesComboBox.getSelectedItem(); - if (selectedCourse == null || selectedCourse.equals(CourseInfo.INVALID_COURSE)) { + Course selectedCourse = (Course)myCoursesComboBox.getSelectedItem(); + if (selectedCourse == null || selectedCourse.equals(Course.INVALID_COURSE)) { setError(CONNECTION_ERROR); } } @@ -255,9 +255,9 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor { myAnchor = anchor; } - public void updateInfoPanel(@NotNull CourseInfo selectedCourseInfo) { - setAuthors(selectedCourseInfo); - myDescriptionPane.setText(selectedCourseInfo.getDescription()); + public void updateInfoPanel(@NotNull Course selectedCourse) { + setAuthors(selectedCourse); + myDescriptionPane.setText(selectedCourse.getDescription()); } /** @@ -268,15 +268,15 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor { private class RefreshActionListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { - final List courses = + final List courses = myGenerator.getCoursesUnderProgress(true, "Refreshing Course List", DefaultProjectFactory.getInstance().getDefaultProject()); - if (!courses.contains(CourseInfo.INVALID_COURSE)) { + if (!courses.contains(Course.INVALID_COURSE)) { refreshCoursesList(courses); } } } - private void refreshCoursesList(@NotNull final List courses) { + private void refreshCoursesList(@NotNull final List courses) { if (courses.isEmpty()) { setError(CONNECTION_ERROR); return; @@ -284,17 +284,16 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor { myCoursesComboBox.removeAllItems(); addCoursesToCombobox(courses); - final CourseInfo selectedCourse = StudyUtils.getFirst(courses); + final Course selectedCourse = StudyUtils.getFirst(courses); if (selectedCourse == null) return; myGenerator.setSelectedCourse(selectedCourse); myGenerator.setCourses(courses); myAvailableCourses = courses; - StudyProjectGenerator.flushCache(myAvailableCourses, false); } - private void addCoursesToCombobox(@NotNull List courses) { - for (CourseInfo courseInfo : courses) { + private void addCoursesToCombobox(@NotNull List courses) { + for (Course courseInfo : courses) { myCoursesComboBox.addItem(courseInfo); } } @@ -309,15 +308,15 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor { @Override public void actionPerformed(ActionEvent e) { JComboBox cb = (JComboBox)e.getSource(); - CourseInfo selectedCourse = (CourseInfo)cb.getSelectedItem(); - if (selectedCourse == null || selectedCourse.equals(CourseInfo.INVALID_COURSE)) { + Course selectedCourse = (Course)cb.getSelectedItem(); + if (selectedCourse == null || selectedCourse.equals(Course.INVALID_COURSE)) { myAuthorLabel.setText(""); myDescriptionPane.setText(""); setError(INVALID_COURSE); return; } updateInfoPanel(selectedCourse); - myCoursesComboBox.removeItem(CourseInfo.INVALID_COURSE); + myCoursesComboBox.removeItem(Course.INVALID_COURSE); myGenerator.setSelectedCourse(selectedCourse); setOK(); @@ -329,12 +328,12 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor { } } - private void setAuthors(CourseInfo selectedCourse) { + private void setAuthors(Course selectedCourse) { final String authorsString = Course.getAuthorsString(selectedCourse.getAuthors()); myAuthorLabel.setText(!StringUtil.isEmptyOrSpaces(authorsString) ? "Author: " + authorsString : ""); } - public JComboBox getCoursesComboBox() { + public JComboBox getCoursesComboBox() { return myCoursesComboBox; } @@ -367,7 +366,7 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor { stepicUser.setPassword(myLoginPanel.getPassword()); StudySettings.getInstance().setUser(stepicUser); myGenerator.setEnrolledCoursesIds(EduAdaptiveStepicConnector.getEnrolledCoursesIds(stepicUser)); - final List courses = myGenerator.getCourses(true); + final List courses = myGenerator.getCourses(true); if (courses != null && myRefreshCourseList) { ApplicationManager.getApplication().invokeLater(() -> refreshCoursesList(courses)); } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java b/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java index 5a855a007786..9a2358a625a9 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java @@ -324,7 +324,6 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D int taskNum = 0; int taskSolved = 0; for (Lesson lesson : lessons) { - if (lesson.getName().equals(EduNames.PYCHARM_ADDITIONAL)) continue; taskNum += lesson.getTaskList().size(); taskSolved += getSolvedTasks(lesson); } diff --git a/python/educational-core/testData/migration/adaptive5.xml b/python/educational-core/testData/migration/adaptive45.after.xml similarity index 100% rename from python/educational-core/testData/migration/adaptive5.xml rename to python/educational-core/testData/migration/adaptive45.after.xml diff --git a/python/educational-core/testData/migration/adaptive4.xml b/python/educational-core/testData/migration/adaptive45.xml similarity index 100% rename from python/educational-core/testData/migration/adaptive4.xml rename to python/educational-core/testData/migration/adaptive45.xml diff --git a/python/educational-core/testData/migration/4.xml b/python/educational-core/testData/migration/fromThirdToForth.after.xml similarity index 100% rename from python/educational-core/testData/migration/4.xml rename to python/educational-core/testData/migration/fromThirdToForth.after.xml diff --git a/python/educational-core/testData/migration/3.xml b/python/educational-core/testData/migration/fromThirdToForth.xml similarity index 100% rename from python/educational-core/testData/migration/3.xml rename to python/educational-core/testData/migration/fromThirdToForth.xml diff --git a/python/educational-core/testData/migration/subtasks5.xml b/python/educational-core/testData/migration/subtasks45.after.xml similarity index 100% rename from python/educational-core/testData/migration/subtasks5.xml rename to python/educational-core/testData/migration/subtasks45.after.xml diff --git a/python/educational-core/testData/migration/subtasks4.xml b/python/educational-core/testData/migration/subtasks45.xml similarity index 100% rename from python/educational-core/testData/migration/subtasks4.xml rename to python/educational-core/testData/migration/subtasks45.xml diff --git a/python/educational-core/testData/migration/theory351To4.after.xml b/python/educational-core/testData/migration/theory351To4.after.xml new file mode 100644 index 000000000000..88c6f6a616de --- /dev/null +++ b/python/educational-core/testData/migration/theory351To4.after.xml @@ -0,0 +1,115 @@ + + + + + + + \ No newline at end of file diff --git a/python/educational-core/testData/migration/theory351To4.xml b/python/educational-core/testData/migration/theory351To4.xml new file mode 100644 index 000000000000..205453ac34d0 --- /dev/null +++ b/python/educational-core/testData/migration/theory351To4.xml @@ -0,0 +1,117 @@ + + + + + + + \ No newline at end of file diff --git a/python/educational-core/testData/migration/theory35To4.after.xml b/python/educational-core/testData/migration/theory35To4.after.xml new file mode 100644 index 000000000000..07edf716d6b2 --- /dev/null +++ b/python/educational-core/testData/migration/theory35To4.after.xml @@ -0,0 +1,115 @@ + + + + + + + \ No newline at end of file diff --git a/python/educational-core/testData/migration/theory35To4.xml b/python/educational-core/testData/migration/theory35To4.xml new file mode 100644 index 000000000000..408fd6301eef --- /dev/null +++ b/python/educational-core/testData/migration/theory35To4.xml @@ -0,0 +1,117 @@ + + + + + + + diff --git a/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java b/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java index 69d9b4eb311d..5be02916f2fe 100644 --- a/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java +++ b/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java @@ -81,7 +81,6 @@ public abstract class CCTestCase extends CodeInsightFixtureTestCase { super.setUp(); Course course = new Course(); course.setName("test course"); - course.setCourseDirectory(getProject().getBasePath()); StudyTaskManager.getInstance(getProject()).setCourse(course); Lesson lesson = new Lesson(); @@ -91,7 +90,7 @@ public abstract class CCTestCase extends CodeInsightFixtureTestCase { task.setIndex(1); lesson.addTask(task); lesson.setIndex(1); - course.getLessons().add(lesson); + course.addLesson(lesson); course.setCourseMode(CCUtils.COURSE_MODE); course.initCourse(false); ApplicationManager.getApplication().runWriteAction(new Runnable() { @@ -118,6 +117,8 @@ public abstract class CCTestCase extends CodeInsightFixtureTestCase { taskFile.setTask(task); task.getTaskFiles().put(name, taskFile); VirtualFile file = copyFileToTask(name); + + taskFile.name = name; myFixture.configureFromExistingVirtualFile(file); Document document = FileDocumentManager.getInstance().getDocument(file); for (AnswerPlaceholder placeholder : getPlaceholders(document, false)) { @@ -125,7 +126,6 @@ public abstract class CCTestCase extends CodeInsightFixtureTestCase { } taskFile.sortAnswerPlaceholders(); StudyUtils.drawAllAnswerPlaceholders(myFixture.getEditor(), taskFile); - CCUtils.createResourceFile(file, StudyTaskManager.getInstance(getProject()).getCourse(), file.getParent()); return file; } @@ -171,6 +171,7 @@ public abstract class CCTestCase extends CodeInsightFixtureTestCase { } document.deleteString(closingMatcher.start(), closingMatcher.end()); document.deleteString(openingMatcher.start(), openingMatcher.end()); + FileDocumentManager.getInstance().saveDocument(document); placeholders.add(answerPlaceholder); pos = answerPlaceholder.getOffset() + answerPlaceholder.getRealLength(); } diff --git a/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/actions/CCShowPreviewTest.java b/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/actions/CCShowPreviewTest.java index 79f8b5710a01..8d685c66498d 100644 --- a/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/actions/CCShowPreviewTest.java +++ b/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/actions/CCShowPreviewTest.java @@ -51,7 +51,7 @@ public class CCShowPreviewTest extends CCTestCase { action.actionPerformed(e); Editor editor = EditorFactory.getInstance().getAllEditors()[1]; Pair> pair = getPlaceholders(name + CCTestsUtil.AFTER_POSTFIX); - assertEquals("Files don't match", editor.getDocument().getText(), pair.getFirst().getText()); + assertEquals("Files don't match", pair.getFirst().getText(), editor.getDocument().getText()); for (AnswerPlaceholder placeholder : pair.getSecond()) { assertNotNull("No highlighter for placeholder:" + CCTestsUtil.getPlaceholderPresentation(placeholder), getHighlighter(editor.getMarkupModel(), placeholder)); } diff --git a/python/educational-core/testSrc/com/jetbrains/edu/learning/StudyMigrationTest.java b/python/educational-core/testSrc/com/jetbrains/edu/learning/StudyMigrationTest.java index 218e2d03efd1..b02860152f93 100644 --- a/python/educational-core/testSrc/com/jetbrains/edu/learning/StudyMigrationTest.java +++ b/python/educational-core/testSrc/com/jetbrains/edu/learning/StudyMigrationTest.java @@ -5,7 +5,9 @@ import com.intellij.util.JdomKt; import org.jdom.Element; import org.jdom.JDOMException; import org.jetbrains.annotations.NotNull; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; import java.io.IOException; import java.nio.file.Path; @@ -14,22 +16,51 @@ import java.nio.file.Paths; import static com.intellij.testFramework.assertions.Assertions.assertThat; public class StudyMigrationTest { + @Rule public TestName name = new TestName(); + @Test public void testFromThirdToForth() throws JDOMException, IOException, StudySerializationUtils.StudyUnrecognizedFormatException { - Element element = JdomKt.loadElement(getTestDataPath().resolve("3.xml")); - assertThat(StudySerializationUtils.Xml.convertToForthVersion(element)).isEqualTo(getTestDataPath().resolve("4.xml")); + doTest(3); } @Test public void testAdaptive45() throws JDOMException, IOException, StudySerializationUtils.StudyUnrecognizedFormatException { - Element element = JdomKt.loadElement(getTestDataPath().resolve("adaptive4.xml")); - assertThat(StudySerializationUtils.Xml.convertToFifthVersion(element)).isEqualTo(getTestDataPath().resolve("adaptive5.xml")); + doTest(4); } @Test public void testSubtasks45() throws JDOMException, IOException, StudySerializationUtils.StudyUnrecognizedFormatException { - Element element = JdomKt.loadElement(getTestDataPath().resolve("subtasks4.xml")); - assertThat(StudySerializationUtils.Xml.convertToFifthVersion(element)).isEqualTo(getTestDataPath().resolve("subtasks5.xml")); + doTest(4); + } + + @Test + public void testTheory35To4() throws JDOMException, IOException, StudySerializationUtils.StudyUnrecognizedFormatException { + doTest(4); + } + + @Test + public void testTheory351To4() throws JDOMException, IOException, StudySerializationUtils.StudyUnrecognizedFormatException { + doTest(4); + } + + private void doTest(int version) throws IOException, JDOMException, StudySerializationUtils.StudyUnrecognizedFormatException { + final String name = PlatformTestUtil.getTestName(this.name.getMethodName(), true); + final Path before = getTestDataPath().resolve(name + ".xml"); + final Path after = getTestDataPath().resolve(name + ".after.xml"); + Element element = JdomKt.loadElement(before); + Element converted = element; + switch (version) { + case 1: + converted = StudySerializationUtils.Xml.convertToSecondVersion(element); + break; + case 3: + converted = StudySerializationUtils.Xml.convertToForthVersion(element); + break; + case 4: + converted = StudySerializationUtils.Xml.convertToFifthVersion(element); + break; + } + assertThat(converted).isEqualTo(after); } @NotNull diff --git a/python/educational-core/testSrc/com/jetbrains/edu/learning/stepic/StudyStepicFormatTest.java b/python/educational-core/testSrc/com/jetbrains/edu/learning/stepic/StudyStepicFormatTest.java index f524d01fdfe6..e7d6a4b112a3 100644 --- a/python/educational-core/testSrc/com/jetbrains/edu/learning/stepic/StudyStepicFormatTest.java +++ b/python/educational-core/testSrc/com/jetbrains/edu/learning/stepic/StudyStepicFormatTest.java @@ -8,7 +8,7 @@ import com.intellij.util.containers.ContainerUtil; import com.jetbrains.edu.learning.StudySerializationUtils; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholderSubtaskInfo; -import com.jetbrains.edu.learning.courseFormat.CourseInfo; +import com.jetbrains.edu.learning.courseFormat.RemoteCourse; import com.jetbrains.edu.learning.courseFormat.TaskFile; import org.jetbrains.annotations.NotNull; import org.junit.Test; @@ -65,9 +65,9 @@ public class StudyStepicFormatTest { EduStepicClient.deserializeStepicResponse(StepicWrappers.CoursesContainer.class, responseString); assertNotNull(container.courses); assertTrue("Incorrect number of courses", container.courses.size() == 4); - List filtered = ContainerUtil.filter(container.courses, info -> EduStepicConnector.canBeOpened(info)); + List filtered = ContainerUtil.filter(container.courses, info -> EduStepicConnector.canBeOpened(info)); assertEquals(ContainerUtil.newArrayList("Adaptive Python", "Introduction to Python", "format2"), - ContainerUtil.map(filtered, CourseInfo::getName)); + ContainerUtil.map(filtered, RemoteCourse::getName)); } @Test diff --git a/python/educational-python/Edu-Python/resources/courses/Introduction to Python.zip b/python/educational-python/Edu-Python/resources/courses/Introduction to Python.zip index 5542a7925fc0..e3c1e160930e 100644 Binary files a/python/educational-python/Edu-Python/resources/courses/Introduction to Python.zip and b/python/educational-python/Edu-Python/resources/courses/Introduction to Python.zip differ diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/coursecreator/CreateFromArchiveProjectStep.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/coursecreator/CreateFromArchiveProjectStep.java index 4a807963ae6d..ea4a9dafdab3 100644 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/coursecreator/CreateFromArchiveProjectStep.java +++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/coursecreator/CreateFromArchiveProjectStep.java @@ -11,7 +11,6 @@ import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; -import com.jetbrains.edu.learning.courseFormat.CourseInfo; import com.jetbrains.python.newProject.steps.PyCharmNewProjectStep; import org.jetbrains.annotations.NotNull; @@ -25,7 +24,7 @@ class CreateFromArchiveProjectStep extends PyCharmNewProjectStep { private final Project myProject; private final Module myModule; - private PyStudyDirectoryProjectGenerator myGenerator = new PyStudyDirectoryProjectGenerator(); + private PyStudyDirectoryProjectGenerator myGenerator = new PyStudyDirectoryProjectGenerator(true); public MyCustomization(Project project, Module module) { @@ -44,10 +43,10 @@ class CreateFromArchiveProjectStep extends PyCharmNewProjectStep { @NotNull @Override protected DirectoryProjectGenerator createEmptyProjectGenerator() { - Course course = StudyTaskManager.getInstance(myProject).getCourse(); - if (course != null) { + Course currentCourse = StudyTaskManager.getInstance(myProject).getCourse(); + if (currentCourse != null) { VirtualFile folder = CCUtils.getGeneratedFilesFolder(myProject, myModule); - String zipName = FileUtil.sanitizeFileName(course.getName()); + String zipName = FileUtil.sanitizeFileName(currentCourse.getName()); if (zipName.isEmpty()) { zipName = EduNames.COURSE; } @@ -55,9 +54,9 @@ class CreateFromArchiveProjectStep extends PyCharmNewProjectStep { CCCreateCourseArchive.createCourseArchive(myProject, myModule, zipName, locationDir, false); String path = FileUtil.join(FileUtil.toSystemDependentName(folder.getPath()), zipName + ".zip"); StudyProjectGenerator generator = myGenerator.getGenerator(); - CourseInfo info = generator.addLocalCourse(path); - assert info != null; - generator.setSelectedCourse(info); + Course course = generator.addLocalCourse(path); + assert course != null; + generator.setSelectedCourse(course); } return myGenerator; } diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java index 08b023c7d613..558c12db1091 100644 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java +++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java @@ -33,10 +33,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.io.File; - -import static com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator.OUR_COURSES_DIR; - public class PyCCProjectGenerator extends PythonProjectGenerator { private static final Logger LOG = Logger.getInstance(PyCCProjectGenerator.class); @@ -60,11 +56,7 @@ public class PyCCProjectGenerator extends PythonProjectGenerator StudyCheckUtils.flushWindows(myTask, myTaskDir)); return super.execute(executor, processStarter, patchers); diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyEduPluginConfigurator.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyEduPluginConfigurator.java index eee62abd65a9..cfd453a0e8cc 100644 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyEduPluginConfigurator.java +++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyEduPluginConfigurator.java @@ -14,22 +14,27 @@ import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiManager; +import com.intellij.util.PathUtil; import com.jetbrains.edu.coursecreator.settings.CCSettings; import com.jetbrains.edu.learning.actions.StudyCheckAction; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; +import java.util.Collections; +import java.util.List; public class PyEduPluginConfigurator implements EduPluginConfigurator { public static final String PYTHON_3 = "3.x"; public static final String PYTHON_2 = "2.x"; private static final String TESTS_PY = "tests.py"; private static final Logger LOG = Logger.getInstance(PyEduPluginConfigurator.class); + private static final String COURSE_NAME = "Introduction to Python.zip"; @NotNull @Override @@ -65,9 +70,8 @@ public class PyEduPluginConfigurator implements EduPluginConfigurator { } @Override - public boolean excludeFromArchive(@NotNull File pathname) { - String name = pathname.getName(); - return name.contains("__pycache__") || name.contains(".pyc"); + public boolean excludeFromArchive(@NotNull String path) { + return path.contains("__pycache__") || path.endsWith(".pyc"); } @Override @@ -126,4 +130,18 @@ public class PyEduPluginConfigurator implements EduPluginConfigurator { public StudyCheckAction getCheckAction() { return new PyStudyCheckAction(); } + + @Override + public List getBundledCoursePaths() { + @NonNls String jarPath = PathUtil.getJarPathForClass(PyEduPluginConfigurator.class); + + if (jarPath.endsWith(".jar")) { + final File jarFile = new File(jarPath); + + File pluginBaseDir = jarFile.getParentFile(); + return Collections.singletonList(new File(new File(pluginBaseDir, "courses"), COURSE_NAME).getPath()); + } + + return Collections.singletonList(new File(new File(jarPath, "courses"), COURSE_NAME).getPath()); + } } diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java index 443c80f20c5e..264596c26b46 100644 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java +++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java @@ -94,9 +94,9 @@ public class PyStudyCheckAction extends StudyCheckAction { } final Course course = myTaskManger.getCourse(); if (course != null && EduNames.STUDY.equals(course.getCourseMode())) { - CommandProcessor.getInstance().runUndoTransparentAction(() -> ApplicationManager.getApplication().runWriteAction(() -> { - StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project); - })); + CommandProcessor.getInstance().runUndoTransparentAction( + () -> ApplicationManager.getApplication().runWriteAction( + () -> StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project))); } } final StudyToolWindow toolWindow = StudyUtils.getStudyToolWindow(project); diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java index a9d7e496138f..7b4b40317bea 100644 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java +++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java @@ -23,8 +23,9 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiManager; import com.intellij.util.BooleanFunction; +import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.CourseInfo; +import com.jetbrains.edu.learning.courseFormat.RemoteCourse; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; import com.jetbrains.edu.learning.stepic.EduStepicConnector; import com.jetbrains.edu.learning.ui.StudyNewProjectPanel; @@ -58,7 +59,12 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator getCourses() { - return myGenerator.getCoursesUnderProgress(false, "Getting Courses", ProjectManager.getInstance().getDefaultProject()); - } - - public void setSelectedCourse(CourseInfo course) { + public void setSelectedCourse(Course course) { myGenerator.setSelectedCourse(course); } @@ -183,12 +184,12 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator beforeProjectGenerated(@Nullable Sdk sdk) { return generator -> { final List enrolledCoursesIds = myGenerator.getEnrolledCoursesIds(); - final CourseInfo course = (CourseInfo)mySettingsPanel.getCoursesComboBox().getSelectedItem(); - if (course == null) return true; - if (course.getId() > 0 && !enrolledCoursesIds.contains(course.getId())) { + final Course course = (Course)mySettingsPanel.getCoursesComboBox().getSelectedItem(); + if (course == null || !(course instanceof RemoteCourse)) return true; + if (((RemoteCourse)course).getId() > 0 && !enrolledCoursesIds.contains(((RemoteCourse)course).getId())) { ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); - return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(course.getId(), + return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(((RemoteCourse)course).getId(), StudySettings.getInstance().getUser())); }, "Creating Course", true, ProjectManager.getInstance().getDefaultProject()); diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyInitialConfigurator.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyInitialConfigurator.java index 922fd3bc2f1b..f94eb2f67989 100644 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyInitialConfigurator.java +++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyInitialConfigurator.java @@ -2,24 +2,18 @@ package com.jetbrains.edu.learning; import com.intellij.codeInsight.CodeInsightSettings; import com.intellij.ide.util.PropertiesComponent; -import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.application.PathManager; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.project.ex.ProjectManagerEx; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.platform.templates.github.ZipUtil; -import com.intellij.util.PathUtil; import com.intellij.util.messages.MessageBus; -import com.jetbrains.edu.learning.actions.PyStudyIntroductionCourseAction; -import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; import org.jetbrains.annotations.NonNls; import java.io.File; -import java.io.IOException; @SuppressWarnings({"UtilityClassWithoutPrivateConstructor", "UtilityClassWithPublicConstructor"}) public class PyStudyInitialConfigurator { - private static final Logger LOG = Logger.getInstance(PyStudyInitialConfigurator.class.getName()); - @NonNls private static final String CONFIGURED_V35 = "StudyPyCharm.InitialConfiguration35"; + @NonNls private static final String CONFIGURED_V40 = "StudyPyCharm.InitialConfiguration40"; /** * @noinspection UnusedParameters @@ -29,42 +23,10 @@ public class PyStudyInitialConfigurator { final PropertiesComponent propertiesComponent, FileTypeManager fileTypeManager, final ProjectManagerEx projectManager) { - final File file = new File(getCoursesRoot(), "Introduction to Python.zip"); - if (!propertiesComponent.getBoolean(CONFIGURED_V35) && file.exists()) { - try { - File[] children = StudyProjectGenerator.OUR_COURSES_DIR.listFiles( - (dir, name) -> name.equals(PyStudyIntroductionCourseAction.INTRODUCTION_TO_PYTHON)); - if (children != null) { - for (File child : children) { - FileUtil.delete(child); - } - } - copyCourse(file, StudyProjectGenerator.OUR_COURSES_DIR); - propertiesComponent.setValue(CONFIGURED_V35, "true"); - } - catch (IOException e) { - LOG.warn("Couldn't copy bundled courses " + e); - } + if (!propertiesComponent.getBoolean(CONFIGURED_V40)) { + final File courses = new File(PathManager.getConfigPath(), "courses"); + FileUtil.delete(courses); + propertiesComponent.setValue(CONFIGURED_V40, "true"); } } - - private static void copyCourse(File bundledCourse, File userCourseDir) throws IOException { - String fileName = bundledCourse.getName(); - String unzippedName = fileName.substring(0, fileName.indexOf(".")); - File courseDir = new File(userCourseDir, unzippedName); - ZipUtil.unzip(null, courseDir, bundledCourse, null, null, true); - } - - private static File getCoursesRoot() { - @NonNls String jarPath = PathUtil.getJarPathForClass(PyStudyInitialConfigurator.class); - if (jarPath.endsWith(".jar")) { - final File jarFile = new File(jarPath); - - - File pluginBaseDir = jarFile.getParentFile(); - return new File(pluginBaseDir, "courses"); - } - - return new File(jarPath, "courses"); - } } diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyShowTutorial.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyShowTutorial.java index 9adf0f45dfb5..0539ea134b28 100644 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyShowTutorial.java +++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyShowTutorial.java @@ -23,32 +23,25 @@ public class PyStudyShowTutorial extends AbstractProjectComponent { @Override public void projectOpened() { - ApplicationManager.getApplication().invokeLater(new DumbAwareRunnable() { - @Override - public void run() { - ApplicationManager.getApplication().runWriteAction(new DumbAwareRunnable() { - @Override - public void run() { - if (PropertiesComponent.getInstance().getBoolean(ourShowPopup, true)) { - final String content = "If you'd like to learn more about PyCharm Edu, " + - "click here to watch a tutorial"; - final Notification notification = new Notification("Watch Tutorials!", "", content, NotificationType.INFORMATION, - new NotificationListener.UrlOpeningListener(true)); - Notifications.Bus.notify(notification); - Balloon balloon = notification.getBalloon(); - if (balloon != null) { - balloon.addListener(new JBPopupAdapter() { - @Override - public void onClosed(LightweightWindowEvent event) { - notification.expire(); - } - }); + ApplicationManager.getApplication().invokeLater((DumbAwareRunnable)() -> ApplicationManager.getApplication().runWriteAction( + (DumbAwareRunnable)() -> { + if (PropertiesComponent.getInstance().getBoolean(ourShowPopup, true)) { + final String content = "If you'd like to learn more about PyCharm Edu, " + + "click here to watch a tutorial"; + final Notification notification = new Notification("Watch Tutorials!", "", content, NotificationType.INFORMATION, + new NotificationListener.UrlOpeningListener(true)); + Notifications.Bus.notify(notification); + Balloon balloon = notification.getBalloon(); + if (balloon != null) { + balloon.addListener(new JBPopupAdapter() { + @Override + public void onClosed(LightweightWindowEvent event) { + notification.expire(); } - notification.whenExpired(() -> PropertiesComponent.getInstance().setValue(ourShowPopup, false, true)); - } + }); } - }); - } - }); + notification.whenExpired(() -> PropertiesComponent.getInstance().setValue(ourShowPopup, false, true)); + } + })); } } diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyTestRunner.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyTestRunner.java index a24db818717f..6720691d37f6 100644 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyTestRunner.java +++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyTestRunner.java @@ -10,7 +10,6 @@ import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.vfs.VirtualFile; import com.jetbrains.edu.learning.checker.StudyTestRunner; import com.jetbrains.edu.learning.core.EduNames; -import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks; import com.jetbrains.python.sdk.PythonSdkType; @@ -28,7 +27,6 @@ public class PyStudyTestRunner extends StudyTestRunner { public Process createCheckProcess(@NotNull final Project project, @NotNull final String executablePath) throws ExecutionException { final Sdk sdk = PythonSdkType.findPythonSdk(ModuleManager.getInstance(project).getModules()[0]); - Course course = myTask.getLesson().getCourse(); PyEduPluginConfigurator configurator = new PyEduPluginConfigurator(); String testsFileName = configurator.getTestFileName(); if (myTask instanceof TaskWithSubtasks) { @@ -50,8 +48,6 @@ public class PyStudyTestRunner extends StudyTestRunner { if (pythonPath != null) { commandLine.setExePath(pythonPath); commandLine.addParameter(testRunner.getPath()); - File resourceFile = new File(course.getCourseDirectory()); - commandLine.addParameter(resourceFile.getPath()); commandLine.addParameter(FileUtil.toSystemDependentName(executablePath)); return commandLine.createProcess(); } diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/actions/PyStudyIntroductionCourseAction.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/actions/PyStudyIntroductionCourseAction.java index 1d44f500627d..d2564a08b358 100644 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/actions/PyStudyIntroductionCourseAction.java +++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/actions/PyStudyIntroductionCourseAction.java @@ -19,15 +19,19 @@ import com.intellij.ide.impl.ProjectUtil; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.Presentation; +import com.jetbrains.edu.learning.EduPluginConfigurator; +import com.jetbrains.edu.learning.PyEduPluginConfigurator; import com.jetbrains.edu.learning.PyStudyDirectoryProjectGenerator; +import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; -import com.jetbrains.edu.learning.courseFormat.CourseInfo; +import com.jetbrains.python.PythonLanguage; import com.jetbrains.python.newProject.steps.ProjectSpecificSettingsStep; import com.jetbrains.python.newProject.steps.PythonGenerateProjectCallback; import icons.InteractiveLearningPythonIcons; import org.jetbrains.annotations.NotNull; import java.io.File; +import java.util.List; public class PyStudyIntroductionCourseAction extends AnAction { @@ -44,7 +48,9 @@ public class PyStudyIntroductionCourseAction extends AnAction { if (projectDir.exists()) { return; } - if (StudyProjectGenerator.getBundledIntro() != null) { + final EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(PythonLanguage.getInstance()); + final List paths = configurator.getBundledCoursePaths(); + if (paths.isEmpty()) { return; } Presentation presentation = e.getPresentation(); @@ -59,8 +65,10 @@ public class PyStudyIntroductionCourseAction extends AnAction { ProjectUtil.openProject(projectDir.getPath(), null, false); } else { - final PyStudyDirectoryProjectGenerator generator = new PyStudyDirectoryProjectGenerator(); - CourseInfo introCourse = StudyProjectGenerator.getBundledIntro(); + final PyStudyDirectoryProjectGenerator generator = new PyStudyDirectoryProjectGenerator(true); + final EduPluginConfigurator configurator = new PyEduPluginConfigurator(); + final String bundledCoursePath = configurator.getBundledCoursePaths().get(0); + Course introCourse = StudyProjectGenerator.getCourse(bundledCoursePath); if (introCourse == null) { return; } diff --git a/python/educational-python/resources/fileTemplates/internal/test_helper.py.ft b/python/educational-python/resources/fileTemplates/internal/test_helper.py.ft index 6a3be496eb18..42bdaa245c70 100644 --- a/python/educational-python/resources/fileTemplates/internal/test_helper.py.ft +++ b/python/educational-python/resources/fileTemplates/internal/test_helper.py.ft @@ -120,39 +120,6 @@ def test_is_not_empty(): failed("The file is empty. Please, reload the task and try again.") -def test_is_initial_text(error_text="You should modify the file"): - """ - Checks that file was modified - """ - if not get_answer_placeholders(): - passed() - return - path = sys.argv[-1] - text = get_initial_text(path) - file_text = get_file_text(path) - - if file_text.strip() == text.strip(): - failed(error_text) - else: - passed() - - -def get_initial_text(path): - """ - Returns the initial task text - """ - course_lib = sys.argv[-2] - test_dir = sys.argv[-3] - import os - lesson_task = os.sep.join(test_dir.split(os.sep)[-3:-1]) - task_dir = os.sep.join(test_dir.split(os.sep)[:-1]) - relative_path = lesson_task + os.sep + path[len(task_dir):] - - # path format is "project_root/lessonX/taskY/.../file.py" - initial_file_path = os.path.join(course_lib, relative_path) - return get_file_text(initial_file_path) - - def test_text_equals(text, error_text): """ Checks that answer equals text. @@ -259,7 +226,6 @@ def do_not_run_on_check(): def run_common_tests(error_text="Please, reload file and try again"): - test_is_initial_text() test_is_not_empty() test_answer_placeholders_text_deleted() test_file_importable() diff --git a/python/educational-python/src/com/jetbrains/python/edu/PyExecuteFileLineMarkerProvider.java b/python/educational-python/src/com/jetbrains/python/edu/PyExecuteFileLineMarkerProvider.java index 8a5292e57cc9..ffc8c6372f42 100644 --- a/python/educational-python/src/com/jetbrains/python/edu/PyExecuteFileLineMarkerProvider.java +++ b/python/educational-python/src/com/jetbrains/python/edu/PyExecuteFileLineMarkerProvider.java @@ -71,7 +71,7 @@ public class PyExecuteFileLineMarkerProvider implements LineMarkerProvider { return PlatformUtils.isPyCharmEducational() ? text : actions.get(0).getTemplatePresentation().getText(); }, null, GutterIconRenderer.Alignment.RIGHT) { - @Nullable + @Override public GutterIconRenderer createGutterRenderer() { return new LineMarkerGutterIconRenderer(this) { diff --git a/python/educational-python/src/com/jetbrains/python/edu/debugger/PyEduConsoleInputFilterProvider.java b/python/educational-python/src/com/jetbrains/python/edu/debugger/PyEduConsoleInputFilterProvider.java index 1f191c949548..1e348cf533eb 100644 --- a/python/educational-python/src/com/jetbrains/python/edu/debugger/PyEduConsoleInputFilterProvider.java +++ b/python/educational-python/src/com/jetbrains/python/edu/debugger/PyEduConsoleInputFilterProvider.java @@ -7,7 +7,6 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.jetbrains.python.debugger.PyRunCythonExtensionsFilter; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.List; @@ -17,7 +16,6 @@ public class PyEduConsoleInputFilterProvider implements ConsoleInputFilterProvid @Override public InputFilter[] getDefaultFilters(@NotNull Project project) { return new InputFilter[]{new InputFilter() { - @Nullable @Override public List> applyFilter(String text, ConsoleViewContentType outputType) { if (outputType.equals(ConsoleViewContentType.SYSTEM_OUTPUT) && !text.contains("exit code")) { diff --git a/python/helpers/pydev/pydevd_plugins/django_debug.py b/python/helpers/pydev/pydevd_plugins/django_debug.py index 32daa83ff324..f50d3db84fd9 100644 --- a/python/helpers/pydev/pydevd_plugins/django_debug.py +++ b/python/helpers/pydev/pydevd_plugins/django_debug.py @@ -332,18 +332,15 @@ def _is_django_exception_break_context(frame): #======================================================================================================================= def can_not_skip(plugin, main_debugger, pydb_frame, frame): - if main_debugger.django_breakpoints and _is_django_render_call(frame): - filename = _get_template_file_name(frame) - django_breakpoints_for_file = main_debugger.django_breakpoints.get(filename) - if django_breakpoints_for_file: - return True - return False + return main_debugger.django_breakpoints and _is_django_render_call(frame) + def has_exception_breaks(plugin): if len(plugin.main_debugger.django_exception_break) > 0: return True return False + def has_line_breaks(plugin): for file, breakpoints in dict_iter_items(plugin.main_debugger.django_breakpoints): if len(breakpoints) > 0: diff --git a/python/pluginMinor/META-INF/python-community-plugin-minor.xml b/python/pluginMinor/META-INF/python-community-plugin-minor.xml index ef0bbf03ed27..d069a661d58b 100644 --- a/python/pluginMinor/META-INF/python-community-plugin-minor.xml +++ b/python/pluginMinor/META-INF/python-community-plugin-minor.xml @@ -2,6 +2,7 @@ + (qNames)); } - protected class MyVirtualFileAdapter extends VirtualFileAdapter { + protected class MyVirtualFileListener implements VirtualFileListener { @Override public void fileCreated(@NotNull VirtualFileEvent event) { clearCache(); diff --git a/python/src/com/jetbrains/python/psi/resolve/PythonSdkPathCache.java b/python/src/com/jetbrains/python/psi/resolve/PythonSdkPathCache.java index 42746fbeea3e..02805a195521 100644 --- a/python/src/com/jetbrains/python/psi/resolve/PythonSdkPathCache.java +++ b/python/src/com/jetbrains/python/psi/resolve/PythonSdkPathCache.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -94,7 +94,7 @@ public class PythonSdkPathCache extends PythonPathCache implements Disposable { myBuiltins.set(null); } }, this); - VirtualFileManager.getInstance().addVirtualFileListener(new MyVirtualFileAdapter(), this); + VirtualFileManager.getInstance().addVirtualFileListener(new MyVirtualFileListener(), this); } @Override diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java index 34a2c34bf748..4c350e72ad2c 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java @@ -375,7 +375,7 @@ public final class PythonSdkType extends SdkType { */ @Nullable public static File findExecutableFile(File parent, String name) { - if (SystemInfo.isWindows || SystemInfo.isOS2) { + if (SystemInfo.isWindows) { for (String suffix : WINDOWS_EXECUTABLE_SUFFIXES) { File file = new File(parent, name + "." + suffix); if (file.exists()) return file; diff --git a/resources-en/src/messages/DebuggerBundle.properties b/resources-en/src/messages/DebuggerBundle.properties index b8608db7ae9d..273e22821e8e 100644 --- a/resources-en/src/messages/DebuggerBundle.properties +++ b/resources-en/src/messages/DebuggerBundle.properties @@ -195,7 +195,7 @@ label.base.renderer.configurable.autoscroll=Autoscroll to new &local variables label.base.renderer.configurable.show.synthetic.fields=S&ynthetic fields label.base.renderer.configurable.show.val.fields.as.locals=$val fields as local &variables label.base.renderer.configurable.sort.alphabetically=Sort a&lphabetically -label.base.renderer.configurable.hide.null.array.elements=Hide &null array elements +label.base.renderer.configurable.hide.null.array.elements=Hide &null elements in arrays and collections label.base.renderer.configurable.show.static.fields=&Static fields label.base.renderer.configurable.show.static.final.fields=Static &final fields label.base.renderer.configurable.show.declared.type=Declared &type diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 8917d1320afb..89c31ce067d9 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -1243,7 +1243,7 @@ - + @@ -1916,6 +1916,14 @@ + + + + + + + + diff --git a/uast/build.gradle b/uast/build.gradle new file mode 100644 index 000000000000..8ae03b7a9302 --- /dev/null +++ b/uast/build.gradle @@ -0,0 +1,95 @@ +buildscript { + ext.kotlin_version = '1.0.5' + ext.intellij_core_version = '171.3019.7' + repositories { + jcenter() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.3' + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7' + classpath 'de.undercouch:gradle-download-task:3.1.2' + } +} + +String getUastVersion() { + return System.getenv("ARTIFACT_VERSION") ?: "1.0" +} + +apply from: 'updateDependencies.gradle' + +allprojects { + group = 'org.jetbrains.uast' + version = getUastVersion() + + apply plugin: 'java' + apply plugin: 'kotlin' + apply plugin: 'com.github.johnrengelman.shadow' + apply plugin: 'com.jfrog.bintray' + apply plugin: "maven-publish" + + configurations { + provided + } + + sourceSets { + main { + compileClasspath += configurations.provided + + java.srcDirs = ['src'] + kotlin.srcDirs = ['src'] + } + + test { + java.srcDirs = ['test'] + kotlin.srcDirs = ['test'] + } + } + + compileJava { + sourceCompatibility = 1.6 + targetCompatibility = 1.6 + } + + repositories { + jcenter() + } + + task sourcesJar(type: Jar, dependsOn: classes) { + classifier = 'sources' + from sourceSets.main.allSource + } + + bintray { + user = System.getenv("BINTRAY_USER") ?: "" + key = System.getenv("BINTRAY_API_KEY") ?: "" + + publications = ['UastPublication'] + + pkg { + repo = 'uast' + name = 'uast' + userOrg = 'kotlin' + licenses = ['Apache-2.0'] + vcsUrl = SCM_URL + + version { + name = getUastVersion() + released = new Date() + } + } + } + + publishing { + publications { + UastPublication(MavenPublication) { + from components.java + groupId 'org.jetbrains.uast' + artifactId project.name + version getUastVersion() + + artifact sourcesJar + } + } + } +} \ No newline at end of file diff --git a/uast/gradle.properties b/uast/gradle.properties new file mode 100644 index 000000000000..2c33935b50ce --- /dev/null +++ b/uast/gradle.properties @@ -0,0 +1 @@ +SCM_URL=https://github.com/JetBrains/uast \ No newline at end of file diff --git a/uast/gradlew b/uast/gradlew new file mode 100755 index 000000000000..91a7e269e19d --- /dev/null +++ b/uast/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/uast/gradlew.bat b/uast/gradlew.bat new file mode 100644 index 000000000000..8a0b282aa688 --- /dev/null +++ b/uast/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/uast/settings.gradle b/uast/settings.gradle new file mode 100644 index 000000000000..7ddee0eacbf6 --- /dev/null +++ b/uast/settings.gradle @@ -0,0 +1 @@ +include ':uast-common', ':uast-java', ':uast-tests' diff --git a/uast/uast-common/src/org/jetbrains/uast/UastContext.kt b/uast/uast-common/src/org/jetbrains/uast/UastContext.kt new file mode 100644 index 000000000000..5d6a54eda809 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/UastContext.kt @@ -0,0 +1,144 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.lang.Language +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Key +import com.intellij.psi.* +import com.intellij.reference.SoftReference + +val CACHED_UELEMENT_KEY = Key.create>("org.jetbrains.uast.cachedElement") + +/** + * Manages the UAST to PSI conversion. + */ +class UastContext(val project: Project) : UastLanguagePlugin { + private companion object { + private val CONTEXT_LANGUAGE = object : Language("UastContextLanguage") {} + } + + override val language: Language + get() = CONTEXT_LANGUAGE + + override val priority: Int + get() = 0 + + val languagePlugins: Collection + get() = UastLanguagePlugin.getInstances() + + fun findPlugin(element: PsiElement): UastLanguagePlugin? { + val language = element.language + return languagePlugins.firstOrNull { it.language == language } + } + + override fun isFileSupported(fileName: String) = languagePlugins.any { it.isFileSupported(fileName) } + + fun getMethod(method: PsiMethod): UMethod = convertWithParent(method)!! + + fun getVariable(variable: PsiVariable): UVariable = convertWithParent(variable)!! + + fun getClass(clazz: PsiClass): UClass = convertWithParent(clazz)!! + + override fun convertElement(element: PsiElement, parent: UElement?, requiredType: Class?): UElement? { + val cachedElement = element.getUserData(CACHED_UELEMENT_KEY)?.get() + if (cachedElement != null) { + return if (requiredType == null || requiredType.isInstance(cachedElement)) cachedElement else null + } + + return findPlugin(element)?.convertElement(element, parent, requiredType) + } + + override fun convertElementWithParent(element: PsiElement, requiredType: Class?): UElement? { + val cachedElement = element.getUserData(CACHED_UELEMENT_KEY)?.get() + if (cachedElement != null) { + return if (requiredType == null || requiredType.isInstance(cachedElement)) cachedElement else null + } + + return findPlugin(element)?.convertElementWithParent(element, requiredType) + } + + override fun getMethodCallExpression( + element: PsiElement, + containingClassFqName: String?, + methodName: String + ): UastLanguagePlugin.ResolvedMethod? { + return findPlugin(element)?.getMethodCallExpression(element, containingClassFqName, methodName) + } + + override fun getConstructorCallExpression( + element: PsiElement, + fqName: String + ): UastLanguagePlugin.ResolvedConstructor? { + return findPlugin(element)?.getConstructorCallExpression(element, fqName) + } + + override fun isExpressionValueUsed(element: UExpression): Boolean { + val language = element.getLanguage() + return (languagePlugins.firstOrNull { it.language == language })?.isExpressionValueUsed(element) ?: false + } + + private tailrec fun UElement.getLanguage(): Language { + psi?.language?.let { return it } + val containingElement = this.uastParent ?: throw IllegalStateException("At least UFile should have a language") + return containingElement.getLanguage() + } +} + +/** + * Converts the element along with its parents to UAST. + */ +fun PsiElement?.toUElement() = + this?.let { ServiceManager.getService(project, UastContext::class.java).convertElementWithParent(this, null) } + +/** + * Converts the element to an UAST element of the given type. Returns null if the PSI element type does not correspond + * to the given UAST element type. + */ +fun PsiElement?.toUElement(cls: Class): T? = + this?.let { ServiceManager.getService(project, UastContext::class.java).convertElementWithParent(this, cls) as T? } + +inline fun PsiElement?.toUElementOfType(): T? = + this?.let { ServiceManager.getService(project, UastContext::class.java).convertElementWithParent(this, T::class.java) as T? } + +/** + * Finds an UAST element of a given type at the given [offset] in the specified file. Returns null if there is no UAST + * element of the given type at the given offset. + */ +fun PsiFile.findUElementAt(offset: Int, cls: Class): T? { + val element = findElementAt(offset) ?: return null + val uElement = element.toUElement() ?: return null + @Suppress("UNCHECKED_CAST") + return uElement.withContainingElements.firstOrNull { cls.isInstance(it) } as T? +} + +/** + * Finds an UAST element of the given type among the parents of the given PSI element. + */ +@JvmOverloads +fun PsiElement?.getUastParentOfType(cls: Class, strict: Boolean = false): T? { + val uElement = this.toUElement() ?: return null + val sequence = if (strict) + (uElement.uastParent?.withContainingElements ?: emptySequence()) + else + uElement.withContainingElements + + @Suppress("UNCHECKED_CAST") + return sequence.firstOrNull { cls.isInstance(it) } as T? +} + +inline fun PsiElement?.getUastParentOfType(strict: Boolean = false): T? = getUastParentOfType(T::class.java, strict) diff --git a/uast/uast-common/src/org/jetbrains/uast/UastLanguagePlugin.kt b/uast/uast-common/src/org/jetbrains/uast/UastLanguagePlugin.kt new file mode 100644 index 000000000000..101a8522f2dc --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/UastLanguagePlugin.kt @@ -0,0 +1,123 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.lang.Language +import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.openapi.extensions.Extensions +import com.intellij.psi.* + +interface UastLanguagePlugin { + companion object { + val extensionPointName: ExtensionPointName = + ExtensionPointName.create("org.jetbrains.uast.uastLanguagePlugin") + + fun getInstances(): Collection { + val rootArea = Extensions.getRootArea() + if (!rootArea.hasExtensionPoint(extensionPointName.name)) return listOf() + return rootArea.getExtensionPoint(extensionPointName).extensions.toList() + } + } + + data class ResolvedMethod(val call: UCallExpression, val method: PsiMethod) + data class ResolvedConstructor(val call: UCallExpression, val constructor: PsiMethod, val clazz: PsiClass) + + val language: Language + + /** + * Checks if the file with the given [fileName] is supported. + * + * @param fileName the source file name. + * @return true, if the file is supported by this converter, false otherwise. + */ + fun isFileSupported(fileName: String): Boolean + + /** + * Returns the converter priority. Might be positive, negative or 0 (Java's is 0). + * UastConverter with the higher priority will be queried earlier. + * + * Priority is useful when a language N wraps its own elements (NElement) to, for example, Java's PsiElements, + * and Java resolves the reference to such wrapped PsiElements, not the original NElement. + * In this case N implementation can handle such wrappers in UastConverter earlier than Java's converter, + * so N language converter will have a higher priority. + */ + val priority: Int + + /** + * Converts a PSI element, the parent of which already has an UAST representation, to UAST. + * + * @param element the element to convert + * @param parent the parent as an UAST element, or null if the element is a file + * @param requiredType the expected type of the result. + * @return the converted element, or null if the element isn't supported or doesn't match the required result type. + */ + fun convertElement(element: PsiElement, parent: UElement?, requiredType: Class? = null): UElement? + + /** + * Converts a PSI element, along with its chain of parents, to UAST. + * + * @param element the element to convert + * @param requiredType the expected type of the result. + * @return the converted element, or null if the element isn't supported or doesn't match the required result type. + */ + fun convertElementWithParent(element: PsiElement, requiredType: Class?): UElement? + + fun getMethodCallExpression( + element: PsiElement, + containingClassFqName: String?, + methodName: String + ): ResolvedMethod? + + fun getConstructorCallExpression( + element: PsiElement, + fqName: String + ) : ResolvedConstructor? + + fun getMethodBody(element: PsiMethod): UExpression? { + if (element is UMethod) return element.uastBody + return (convertElementWithParent(element, null) as? UMethod)?.uastBody + } + + fun getInitializerBody(element: PsiClassInitializer): UExpression { + if (element is UClassInitializer) return element.uastBody + return (convertElementWithParent(element, null) as? UClassInitializer)?.uastBody ?: UastEmptyExpression + } + + fun getInitializerBody(element: PsiVariable): UExpression? { + if (element is UVariable) return element.uastInitializer + return (convertElementWithParent(element, null) as? UVariable)?.uastInitializer + } + + /** + * Returns true if the expression value is used. + * Do not rely on this property too much, its value can be approximate in some cases. + */ + fun isExpressionValueUsed(element: UExpression): Boolean +} + +inline fun UastLanguagePlugin.convertOpt(element: PsiElement?, parent: UElement?): T? { + if (element == null) return null + return convertElement(element, parent) as? T +} + +inline fun UastLanguagePlugin.convert(element: PsiElement, parent: UElement?): T { + return convertElement(element, parent, T::class.java) as T +} + +inline fun UastLanguagePlugin.convertWithParent(element: PsiElement?): T? { + if (element == null) return null + return convertElementWithParent(element, T::class.java) as? T +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/UastUtils.kt b/uast/uast-common/src/org/jetbrains/uast/UastUtils.kt new file mode 100644 index 000000000000..c0fb99739dab --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/UastUtils.kt @@ -0,0 +1,148 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:JvmMultifileClass +@file:JvmName("UastUtils") +package org.jetbrains.uast + +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.vfs.VfsUtilCore +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiNamedElement +import com.intellij.psi.util.PsiTreeUtil +import java.io.File + +inline fun UElement.getParentOfType(strict: Boolean = true): T? = getParentOfType(T::class.java, strict) + +@JvmOverloads +fun UElement.getParentOfType(parentClass: Class, strict: Boolean = true): T? { + var element = (if (strict) uastParent else this) ?: return null + while (true) { + if (parentClass.isInstance(element)) { + @Suppress("UNCHECKED_CAST") + return element as T + } + element = element.uastParent ?: return null + } +} + +fun UElement.getParentOfType( + parentClass: Class, + strict: Boolean = true, + vararg terminators: Class +): T? { + var element = (if (strict) uastParent else this) ?: return null + while (true) { + if (parentClass.isInstance(element)) { + @Suppress("UNCHECKED_CAST") + return element as T + } + if (terminators.any { it.isInstance(element) }) { + return null + } + element = element.uastParent ?: return null + } +} + +fun UElement.getParentOfType( + strict: Boolean = true, + firstParentClass: Class, + vararg parentClasses: Class +): T? { + var element = (if (strict) uastParent else this) ?: return null + while (true) { + if (firstParentClass.isInstance(element)) { + @Suppress("UNCHECKED_CAST") + return element as T + } + if (parentClasses.any { it.isInstance(element) }) { + @Suppress("UNCHECKED_CAST") + return element as T + } + element = element.uastParent ?: return null + } +} + +fun UElement.getContainingFile() = getParentOfType(UFile::class.java) + +fun UElement.getContainingUClass() = getParentOfType(UClass::class.java) +fun UElement.getContainingUMethod() = getParentOfType(UMethod::class.java) +fun UElement.getContainingUVariable() = getParentOfType(UVariable::class.java) + +fun UElement.getContainingMethod() = getContainingUMethod()?.psi +fun UElement.getContainingClass() = getContainingUClass()?.psi +fun UElement.getContainingVariable() = getContainingUVariable()?.psi + +fun PsiElement?.getContainingClass() = this?.let { PsiTreeUtil.getParentOfType(it, PsiClass::class.java) } + +fun UElement.isChildOf(probablyParent: UElement?, strict: Boolean = false): Boolean { + tailrec fun isChildOf(current: UElement?, probablyParent: UElement): Boolean { + return when (current) { + null -> false + probablyParent -> true + else -> isChildOf(current.uastParent, probablyParent) + } + } + + if (probablyParent == null) return false + return isChildOf(if (strict) this else uastParent, probablyParent) +} + +/** + * Resolves the receiver element if it implements [UResolvable]. + * + * @return the resolved element, or null if the element was not resolved, or if the receiver element is not an [UResolvable]. + */ +fun UElement.tryResolve(): PsiElement? = (this as? UResolvable)?.resolve() + +fun UElement.tryResolveNamed(): PsiNamedElement? = (this as? UResolvable)?.resolve() as? PsiNamedElement + +fun UElement.tryResolveUDeclaration(context: UastContext): UDeclaration? { + return (this as? UResolvable)?.resolve()?.let { context.convertElementWithParent(it, null) as? UDeclaration } +} + +fun UReferenceExpression?.getQualifiedName() = (this?.resolve() as? PsiClass)?.qualifiedName + +/** + * Returns the String expression value, or null if the value can't be calculated or if the calculated value is not a String. + */ +fun UExpression.evaluateString(): String? = evaluate() as? String + +/** + * Get a physical [File] for this file, or null if there is no such file on disk. + */ +fun UFile.getIoFile(): File? = psi.virtualFile?.let { VfsUtilCore.virtualToIoFile(it) } + +tailrec fun UElement.getUastContext(): UastContext { + val psi = this.psi + if (psi != null) { + return ServiceManager.getService(psi.project, UastContext::class.java) ?: error("UastContext not found") + } + + return (uastParent ?: error("PsiElement should exist at least for UFile")).getUastContext() +} + +tailrec fun UElement.getLanguagePlugin(): UastLanguagePlugin { + val psi = this.psi + if (psi != null) { + val uastContext = ServiceManager.getService(psi.project, UastContext::class.java) ?: error("UastContext not found") + return uastContext.findPlugin(psi) ?: error("Language plugin was not found for $this (${this.javaClass.name})") + } + + return (uastParent ?: error("PsiElement should exist at least for UFile")).getLanguagePlugin() +} + +fun Collection.toPsiElements() = mapNotNull { it.psi } diff --git a/uast/uast-common/src/org/jetbrains/uast/baseElements/UComment.kt b/uast/uast-common/src/org/jetbrains/uast/baseElements/UComment.kt new file mode 100644 index 000000000000..67bc9d3608f1 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/baseElements/UComment.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.PsiElement +import org.jetbrains.uast.internal.log + +class UComment(override val psi: PsiElement, override val uastParent: UElement) : UElement { + val text: String + get() = asSourceString() + + override fun asLogString() = log() + + override fun asRenderString(): String = asSourceString() + override fun asSourceString(): String = psi.text +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/baseElements/UElement.kt b/uast/uast-common/src/org/jetbrains/uast/baseElements/UElement.kt new file mode 100644 index 000000000000..75a0f28f5fd0 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/baseElements/UElement.kt @@ -0,0 +1,108 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.PsiElement +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * The common interface for all Uast elements. + */ +interface UElement { + /** + * Returns the element parent. + */ + val uastParent: UElement? + + /** + * Returns the PSI element underlying this element. Note that some UElements are synthetic and do not have + * an underlying PSI element; this doesn't mean that they are invalid. + */ + val psi: PsiElement? + + /** + * Returns true if this element is valid, false otherwise. + */ + val isPsiValid: Boolean + get() = psi?.isValid ?: true + + /** + * Returns the list of comments for this element. + */ + val comments: List + get() = emptyList() + + /** + * Returns the log string (usually one line containing the class name and some additional information). + * + * Examples: + * UWhileExpression + * UBinaryExpression (>) + * UCallExpression (println) + * USimpleReferenceExpression (i) + * ULiteralExpression (5) + * + * @return the expression tree for this element. + * @see [UIfExpression] for example. + */ + fun asLogString(): String + + /** + * Returns the string in pseudo-code. + * + * Output example (should be something like this): + * while (i > 5) { + * println("Hello, world") + * i-- + * } + * + * @return the rendered text. + * @see [UIfExpression] for example. + */ + fun asRenderString(): String = asLogString() + + /** + * Returns the string as written in the source file. + * Use this String only for logging and diagnostic text messages. + * + * @return the original text. + */ + fun asSourceString(): String = asRenderString() + + /** + * Passes the element to the specified visitor. + * + * @param visitor the visitor to pass the element to. + */ + fun accept(visitor: UastVisitor) { + visitor.visitElement(this) + visitor.afterVisitElement(this) + } + + /** + * Passes the element to the specified typed visitor. + * + * @param visitor the visitor to pass the element to. + */ + fun accept(visitor: UastTypedVisitor, data: D): R = visitor.visitElement(this, data) +} + +/** + * Returns a sequence including this element and its containing elements. + */ +val UElement.withContainingElements: Sequence + get() = generateSequence(this, UElement::uastParent) diff --git a/uast/uast-common/src/org/jetbrains/uast/baseElements/UExpression.kt b/uast/uast-common/src/org/jetbrains/uast/baseElements/UExpression.kt new file mode 100644 index 000000000000..a94693db5fe1 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/baseElements/UExpression.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiType +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents an expression or statement (which is considered as an expression in Uast). + */ +interface UExpression : UElement, UAnnotated { + /** + * Returns the expression value or null if the value can't be calculated. + */ + fun evaluate(): Any? = null + + /** + * Returns expression type, or null if type can not be inferred, or if this expression is a statement. + */ + fun getExpressionType(): PsiType? = null + + override fun accept(visitor: UastVisitor) { + visitor.visitElement(this) + visitor.afterVisitElement(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = visitor.visitExpression(this, data) +} + +/** + * Represents an annotated element. + */ +interface UAnnotated : UElement { + /** + * Returns the list of annotations applied to the current element. + */ + val annotations: List + + /** + * Looks up for annotation element using the annotation qualified name. + * + * @param fqName the qualified name to search + * @return the first annotation element with the specified qualified name, or null if there is no annotation with such name. + */ + fun findAnnotation(fqName: String): UAnnotation? = annotations.firstOrNull { it.qualifiedName == fqName } +} + +/** + * Represents a labeled element. + */ +interface ULabeled : UElement { + /** + * Returns the label name, or null if the label is empty. + */ + val label: String? + + /** + * Returns the label identifier, or null if the label is empty. + */ + val labelIdentifier: UIdentifier? +} + +/** + * In some cases (user typing, syntax error) elements, which are supposed to exist, are missing. + * The obvious example — the lack of the condition expression in [UIfExpression], e.g. `if () return`. + * [UIfExpression.condition] is required to return not-null values, + * and Uast implementation should return something instead of `null` in this case. + * + * Use [UastEmptyExpression] in this case. + */ +object UastEmptyExpression : UExpression { + override val uastParent: UElement? + get() = null + + override val annotations: List + get() = emptyList() + + override val psi: PsiElement? + get() = null + + override fun asLogString() = log() +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/baseElements/UIdentifier.kt b/uast/uast-common/src/org/jetbrains/uast/baseElements/UIdentifier.kt new file mode 100644 index 000000000000..fde8d72e4e9b --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/baseElements/UIdentifier.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast + +import com.intellij.psi.PsiElement +import org.jetbrains.uast.internal.log + +class UIdentifier( + override val psi: PsiElement?, + override val uastParent: UElement? +) : UElement { + /** + * Returns the identifier name. + */ + val name: String + get() = psi?.text ?: "" + + override fun asLogString() = log("Identifier ($name)") +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/baseElements/UResolvable.kt b/uast/uast-common/src/org/jetbrains/uast/baseElements/UResolvable.kt new file mode 100644 index 000000000000..ada0da0b7c46 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/baseElements/UResolvable.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.PsiElement + +interface UResolvable { + /** + * Resolve the reference. + * Note that the reference is *always* resolved to an unwrapped [PsiElement], never to a [UElement]. + * + * @return the resolved element, or null if the reference couldn't be resolved. + */ + fun resolve(): PsiElement? +} + +fun UResolvable.resolveToUElement(): UElement? = resolve().toUElement() diff --git a/uast/uast-common/src/org/jetbrains/uast/baseElements/UastErrorType.kt b/uast/uast-common/src/org/jetbrains/uast/baseElements/UastErrorType.kt new file mode 100644 index 000000000000..33782a61e0bf --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/baseElements/UastErrorType.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.PsiType +import com.intellij.psi.PsiTypeVisitor + +object UastErrorType : PsiType(emptyArray()) { + override fun getInternalCanonicalText() = "" + override fun equalsToText(text: String) = false + override fun getCanonicalText() = internalCanonicalText + override fun getPresentableText() = canonicalText + override fun isValid() = false + override fun getResolveScope() = null + override fun getSuperTypes() = emptyArray() + + override fun accept(visitor: PsiTypeVisitor) = visitor.visitType(this) +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/controlStructures/UDoWhileExpression.kt b/uast/uast-common/src/org/jetbrains/uast/controlStructures/UDoWhileExpression.kt new file mode 100644 index 000000000000..bc5f8504620b --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/controlStructures/UDoWhileExpression.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represent a + * + * `do { + * // body + * } while (expr)` + * + * loop expression. + */ +interface UDoWhileExpression : ULoopExpression { + /** + * Returns the loop post-condition. + */ + val condition: UExpression + + /** + * Returns an identifier for the 'do' keyword. + */ + val doIdentifier: UIdentifier + + /** + * Returns an identifier for the 'while' keyword. + */ + val whileIdentifier: UIdentifier + + override fun accept(visitor: UastVisitor) { + if (visitor.visitDoWhileExpression(this)) return + annotations.acceptList(visitor) + condition.accept(visitor) + body.accept(visitor) + visitor.afterVisitDoWhileExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitDoWhileExpression(this, data) + + override fun asRenderString() = buildString { + append("do ") + append(body.asRenderString()) + appendln("while (${condition.asRenderString()})") + } + + override fun asLogString() = log() +} diff --git a/uast/uast-common/src/org/jetbrains/uast/controlStructures/UForEachExpression.kt b/uast/uast-common/src/org/jetbrains/uast/controlStructures/UForEachExpression.kt new file mode 100644 index 000000000000..880030b077c9 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/controlStructures/UForEachExpression.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a + * + * `for (element : collectionOfElements) { + * // body + * }` + * + * loop expression. + */ +interface UForEachExpression : ULoopExpression { + /** + * Returns the loop variable. + */ + val variable: UParameter + + /** + * Returns the iterated value (collection, sequence, iterable etc.) + */ + val iteratedValue: UExpression + + /** + * Returns the identifier for the 'for' ('foreach') keyword. + */ + val forIdentifier: UIdentifier + + override fun accept(visitor: UastVisitor) { + if (visitor.visitForEachExpression(this)) return + annotations.acceptList(visitor) + iteratedValue.accept(visitor) + body.accept(visitor) + visitor.afterVisitForEachExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitForEachExpression(this, data) + + override fun asRenderString() = buildString { + append("for (") + append(variable.name) + append(" : ") + append(iteratedValue.asRenderString()) + append(") ") + append(body.asRenderString()) + } + + override fun asLogString() = log() +} diff --git a/uast/uast-common/src/org/jetbrains/uast/controlStructures/UForExpression.kt b/uast/uast-common/src/org/jetbrains/uast/controlStructures/UForExpression.kt new file mode 100644 index 000000000000..d32c3969dfee --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/controlStructures/UForExpression.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a + * + * `for (initDeclarations; loopCondition; update) { + * // body + * }` + * + * loop expression. + */ +interface UForExpression : ULoopExpression { + /** + * Returns the [UExpression] containing variable declarations, or null if the are no variables declared. + */ + val declaration: UExpression? + + /** + * Returns the loop condition, or null if the condition is empty. + */ + val condition: UExpression? + + /** + * Returns the loop update expression(s). + */ + val update: UExpression? + + /** + * Returns the identifier for the 'for' keyword. + */ + val forIdentifier: UIdentifier + + override fun accept(visitor: UastVisitor) { + if (visitor.visitForExpression(this)) return + annotations.acceptList(visitor) + declaration?.accept(visitor) + condition?.accept(visitor) + update?.accept(visitor) + body.accept(visitor) + visitor.afterVisitForExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitForExpression(this, data) + + override fun asRenderString() = buildString { + append("for (") + declaration?.let { append(it.asRenderString()) } + append("; ") + condition?.let { append(it.asRenderString()) } + append("; ") + update?.let { append(it.asRenderString()) } + append(") ") + append(body.asRenderString()) + } + + override fun asLogString() = log() +} diff --git a/uast/uast-common/src/org/jetbrains/uast/controlStructures/UIfExpression.kt b/uast/uast-common/src/org/jetbrains/uast/controlStructures/UIfExpression.kt new file mode 100644 index 000000000000..94a819be78e5 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/controlStructures/UIfExpression.kt @@ -0,0 +1,101 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents + * + * `if (condition) { + * // do if true + * } else { + * // do if false + * }` + * + * and + * + * `condition : trueExpression ? falseExpression` + * + * condition expressions. + */ +interface UIfExpression : UExpression { + /** + * Returns the condition expression. + */ + val condition: UExpression + + /** + * Returns the expression which is executed if the condition is true, or null if the expression is empty. + */ + val thenExpression: UExpression? + + /** + * Returns the expression which is executed if the condition is false, or null if the expression is empty. + */ + val elseExpression: UExpression? + + /** + * Returns true if the expression is ternary (condition ? trueExpression : falseExpression). + */ + val isTernary: Boolean + + /** + * Returns an identifier for the 'if' keyword. + */ + val ifIdentifier: UIdentifier + + /** + * Returns an identifier for the 'else' keyword, or null if the conditional expression has not the 'else' part. + */ + val elseIdentifier: UIdentifier? + + override fun accept(visitor: UastVisitor) { + if (visitor.visitIfExpression(this)) return + annotations.acceptList(visitor) + condition.accept(visitor) + thenExpression?.accept(visitor) + elseExpression?.accept(visitor) + visitor.afterVisitIfExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitIfExpression(this, data) + + override fun asLogString() = log() + + override fun asRenderString() = buildString { + if (isTernary) { + append("(" + condition.asRenderString() + ")") + append(" ? ") + append("(" + (thenExpression?.asRenderString() ?: "") + ")") + append(" : ") + append("(" + (elseExpression?.asRenderString() ?: "") + ")") + } else { + append("if (${condition.asRenderString()}) ") + thenExpression?.let { append(it.asRenderString()) } + val elseBranch = elseExpression + if (elseBranch != null && elseBranch !is UastEmptyExpression) { + if (thenExpression !is UBlockExpression) append(" ") + append("else ") + append(elseBranch.asRenderString()) + } + } + } +} diff --git a/uast/uast-common/src/org/jetbrains/uast/controlStructures/ULoopExpression.kt b/uast/uast-common/src/org/jetbrains/uast/controlStructures/ULoopExpression.kt new file mode 100644 index 000000000000..926799aa6be8 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/controlStructures/ULoopExpression.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.visitor.UastTypedVisitor + +/** + * Represents a loop expression. + */ +interface ULoopExpression : UExpression { + /** + * Returns the loop body [UExpression]. + */ + val body: UExpression + + override fun accept(visitor: UastTypedVisitor, data: D) = visitor.visitLoopExpression(this, data) +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/controlStructures/USwitchExpression.kt b/uast/uast-common/src/org/jetbrains/uast/controlStructures/USwitchExpression.kt new file mode 100644 index 000000000000..4a3da8ea6351 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/controlStructures/USwitchExpression.kt @@ -0,0 +1,122 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a + * + * ` switch (expression) { + * case value1 -> expr1 + * case value2 -> expr2 + * ... + * else -> exprElse + * } + * + * conditional expression. + */ +interface USwitchExpression : UExpression { + /** + Returns the expression on which the `switch` expression is performed. + */ + val expression: UExpression? + + /** + Returns the switch body. + The body should contain [USwitchClauseExpression] expressions. + */ + val body: UExpressionList + + /** + * Returns an identifier for the 'switch' ('case', 'when', ...) keyword. + */ + val switchIdentifier: UIdentifier + + override fun accept(visitor: UastVisitor) { + if (visitor.visitSwitchExpression(this)) return + annotations.acceptList(visitor) + expression?.accept(visitor) + body.accept(visitor) + visitor.afterVisitSwitchExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitSwitchExpression(this, data) + + override fun asLogString() = log() + + override fun asRenderString() = buildString { + val expr = expression?.let { "(" + it.asRenderString() + ") " } ?: "" + appendln("switch $expr") + appendln(body.asRenderString()) + } +} + +/** + * Represents a [USwitchExpression] clause. + * [USwitchClauseExpression] does not contain the clause body, + * and the actual body expression should be the next element in the parent expression list. + */ +interface USwitchClauseExpression : UExpression { + /** + * Returns the list of values for this clause, or null if the are no values for this close + * (for example, for the `else` clause). + */ + val caseValues: List + + override fun accept(visitor: UastVisitor) { + if (visitor.visitSwitchClauseExpression(this)) return + annotations.acceptList(visitor) + caseValues.acceptList(visitor) + visitor.afterVisitSwitchClauseExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitSwitchClauseExpression(this, data) + + override fun asRenderString() = caseValues.joinToString { it.asRenderString() } + " -> " + + override fun asLogString() = "USwitchClauseExpression" +} + +/** + * Represents a [USwitchExpression] clause with the body. + * [USwitchClauseExpressionWithBody], comparing with [USwitchClauseExpression], contains the body expression. + * + * Implementing this interface *is the right way* to support `switch` clauses in your language. + */ +interface USwitchClauseExpressionWithBody : USwitchClauseExpression { + /** + * Returns the body expression for this clause. + */ + val body: UExpressionList + + override fun accept(visitor: UastVisitor) { + if (visitor.visitSwitchClauseExpression(this)) return + annotations.acceptList(visitor) + caseValues.acceptList(visitor) + body.accept(visitor) + visitor.afterVisitSwitchClauseExpression(this) + } + + override fun asRenderString() = caseValues.joinToString { it.asRenderString() } + " -> " + body.asRenderString() + + override fun asLogString() = log() +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/controlStructures/UTryExpression.kt b/uast/uast-common/src/org/jetbrains/uast/controlStructures/UTryExpression.kt new file mode 100644 index 000000000000..617ddc34504f --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/controlStructures/UTryExpression.kt @@ -0,0 +1,145 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.PsiType +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents + * + * `try { + * // tryClause body + * } catch (e: Type1, Type2 ... TypeN) { + * // catchClause1 body + * } ... { + * finally { + * //finallyBody + * }` + * + * and + * + * `try (resource1, ..., resourceN) { + * // tryClause body + * }` + * + * expressions. + */ +interface UTryExpression : UExpression { + /** + * Returns `true` if the try expression is a try-with-resources expression. + */ + val hasResources: Boolean + + /** + * Returns the list of resource variables declared in this expression, or an empty list if this expression is not a `try-with-resources` expression. + */ + val resourceVariables: List + + /** + * Returns the `try` clause expression. + */ + val tryClause: UExpression + + /** + * Returns the `catch` clauses [UCatchClause] expression list. + */ + val catchClauses: List + + /** + * Returns the `finally` clause expression, or null if the `finally` clause is absent. + */ + val finallyClause: UExpression? + + /** + * Returns an identifier for the 'try' keyword. + */ + val tryIdentifier: UIdentifier + + /** + * Returns an identifier for the 'finally' keyword, or null if the 'try' expression has not a 'finally' clause. + */ + val finallyIdentifier: UIdentifier? + + override fun accept(visitor: UastVisitor) { + if (visitor.visitTryExpression(this)) return + annotations.acceptList(visitor) + resourceVariables.acceptList(visitor) + tryClause.accept(visitor) + catchClauses.acceptList(visitor) + finallyClause?.accept(visitor) + visitor.afterVisitTryExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitTryExpression(this, data) + + override fun asRenderString() = buildString { + append("try ") + if (hasResources) { + append("(") + append(resourceVariables.joinToString("\n") { it.asRenderString() }) + append(")") + } + appendln(tryClause.asRenderString().trim('\n', '\r')) + catchClauses.forEach { appendln(it.asRenderString().trim('\n', '\r')) } + finallyClause?.let { append("finally ").append(it.asRenderString().trim('\n', '\r')) } + } + + override fun asLogString() = log(if (hasResources) "with resources" else "") +} + +/** + * Represents the `catch` clause in [UTryExpression]. + */ +interface UCatchClause : UElement { + /** + * Returns the `catch` clause body expression. + */ + val body: UExpression + + /** + * Returns the exception parameter variables for this `catch` clause. + */ + val parameters: List + + /** + * Returns the exception type references for this `catch` clause. + */ + val typeReferences: List + + /** + * Returns the expression types for this `catch` clause. + */ + val types: List + get() = typeReferences.map { it.type } + + override fun accept(visitor: UastVisitor) { + if (visitor.visitCatchClause(this)) return + body.accept(visitor) + visitor.afterVisitCatchClause(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitCatchClause(this, data) + + override fun asLogString() = log(parameters.joinToString { it.name ?: "" }) + + override fun asRenderString() = "catch (e) " + body.asRenderString() +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/controlStructures/UWhileExpression.kt b/uast/uast-common/src/org/jetbrains/uast/controlStructures/UWhileExpression.kt new file mode 100644 index 000000000000..db1e0e59e88e --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/controlStructures/UWhileExpression.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a + * + * `while (condition) { + * // body + * }` + * + * expression. + */ +interface UWhileExpression : ULoopExpression { + /** + * Returns the loop condition. + */ + val condition: UExpression + + /** + * Returns an identifier for the 'while' keyword. + */ + val whileIdentifier: UIdentifier + + override fun accept(visitor: UastVisitor) { + if (visitor.visitWhileExpression(this)) return + annotations.acceptList(visitor) + condition.accept(visitor) + body.accept(visitor) + visitor.afterVisitWhileExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitWhileExpression(this, data) + + override fun asRenderString() = buildString { + append("while (${condition.asRenderString()}) ") + append(body.asRenderString()) + } + + override fun asLogString() = log() +} diff --git a/uast/uast-common/src/org/jetbrains/uast/declarations/UAnnotation.kt b/uast/uast-common/src/org/jetbrains/uast/declarations/UAnnotation.kt new file mode 100644 index 000000000000..48a624c1a62f --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/declarations/UAnnotation.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.PsiClass +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * An annotation wrapper to be used in [UastVisitor]. + */ +interface UAnnotation : UElement, UResolvable { + /** + * Returns the annotation qualified name. + */ + val qualifiedName: String? + + /** + * Returns the annotation class, or null if the class reference was not resolved. + */ + override fun resolve(): PsiClass? + + /** + * Returns the annotation values. + */ + val attributeValues: List + + fun findAttributeValue(name: String?): UExpression? + + fun findDeclaredAttributeValue(name: String?): UExpression? + + override fun asRenderString() = buildString { + append("@") + append(qualifiedName) + if(attributeValues.isNotEmpty()) { + attributeValues.joinTo( + buffer = this, + prefix = "(", + postfix = ")", + transform = UNamedExpression::asRenderString) + } + } + + override fun asLogString() = log("fqName = $qualifiedName") + + override fun accept(visitor: UastVisitor) { + if (visitor.visitAnnotation(this)) return + attributeValues.acceptList(visitor) + visitor.afterVisitAnnotation(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitAnnotation(this, data) +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/declarations/UClass.kt b/uast/uast-common/src/org/jetbrains/uast/declarations/UClass.kt new file mode 100644 index 000000000000..fb47f0c178af --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/declarations/UClass.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.PsiAnonymousClass +import com.intellij.psi.PsiClass +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * A class wrapper to be used in [UastVisitor]. + */ +interface UClass : UDeclaration, PsiClass { + override val psi: PsiClass + + /** + * Returns a [UClass] wrapper of the superclass of this class, or null if this class is [java.lang.Object]. + */ + override fun getSuperClass(): UClass? { + val superClass = psi.superClass ?: return null + return getUastContext().convertWithParent(superClass) + } + + val uastSuperTypes: List + + /** + * Returns [UDeclaration] wrappers for the class declarations. + */ + val uastDeclarations: List + + override fun getFields(): Array = + psi.fields.map { getLanguagePlugin().convert(it, this) }.toTypedArray() + + override fun getInitializers(): Array = + psi.initializers.map { getLanguagePlugin().convert(it, this) }.toTypedArray() + + override fun getMethods(): Array = + psi.methods.map { getLanguagePlugin().convert(it, this) }.toTypedArray() + + override fun getInnerClasses(): Array = + psi.innerClasses.map { getLanguagePlugin().convert(it, this) }.toTypedArray() + + override fun asLogString() = log("name = $name") + + override fun accept(visitor: UastVisitor) { + if (visitor.visitClass(this)) return + annotations.acceptList(visitor) + uastDeclarations.acceptList(visitor) + visitor.afterVisitClass(this) + } + + override fun asRenderString() = buildString { + append(psi.renderModifiers()) + val kind = when { + psi.isAnnotationType -> "annotation" + psi.isInterface -> "interface" + psi.isEnum -> "enum" + else -> "class" + } + append(kind).append(' ').append(psi.name) + val superTypes = uastSuperTypes + if (superTypes.isNotEmpty()) { + append(" : ") + append(superTypes.joinToString { it.asRenderString() }) + } + appendln(" {") + uastDeclarations.forEachIndexed { index, declaration -> + appendln(declaration.asRenderString().withMargin) + } + append("}") + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitClass(this, data) +} + +interface UAnonymousClass : UClass, PsiAnonymousClass { + override val psi: PsiAnonymousClass +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/declarations/UClassInitializer.kt b/uast/uast-common/src/org/jetbrains/uast/declarations/UClassInitializer.kt new file mode 100644 index 000000000000..f50a44b519db --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/declarations/UClassInitializer.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.PsiClassInitializer + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * A class initializer wrapper to be used in [UastVisitor]. + */ +interface UClassInitializer : UDeclaration, PsiClassInitializer { + override val psi: PsiClassInitializer + + /** + * Returns the body of this class initializer. + */ + val uastBody: UExpression + + @Deprecated("Use uastBody instead.", ReplaceWith("uastBody")) + override fun getBody() = psi.body + + override fun accept(visitor: UastVisitor) { + if (visitor.visitInitializer(this)) return + annotations.acceptList(visitor) + uastBody.accept(visitor) + visitor.afterVisitInitializer(this) + } + + override fun asRenderString() = buildString { + append(modifierList) + appendln(uastBody.asRenderString().withMargin) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitClassInitializer(this, data) + + override fun asLogString() = log("isStatic = $isStatic") +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/declarations/UDeclaration.kt b/uast/uast-common/src/org/jetbrains/uast/declarations/UDeclaration.kt new file mode 100644 index 000000000000..2d94b2719440 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/declarations/UDeclaration.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiModifier +import com.intellij.psi.PsiModifierListOwner +import org.jetbrains.uast.visitor.UastTypedVisitor + +/** + * A [PsiElement] declaration wrapper. + */ +interface UDeclaration : UElement, PsiModifierListOwner, UAnnotated { + /** + * Returns the original declaration (which is *always* unwrapped, never a [UDeclaration]). + */ + override val psi: PsiModifierListOwner + + override fun getOriginalElement(): PsiElement? = psi.originalElement + + /** + * Returns the declaration name identifier, or null if the declaration is anonymous. + */ + val uastAnchor: UElement? + + /** + * Returns `true` if this declaration has a [PsiModifier.STATIC] modifier. + */ + val isStatic: Boolean + get() = hasModifierProperty(PsiModifier.STATIC) + + /** + * Returns `true` if this declaration has a [PsiModifier.FINAL] modifier. + */ + val isFinal: Boolean + get() = hasModifierProperty(PsiModifier.FINAL) + + /** + * Returns a declaration visibility. + */ + val visibility: UastVisibility + get() = UastVisibility[this] + + override fun accept(visitor: UastTypedVisitor, data: D) = visitor.visitDeclaration(this, data) +} + +fun UElement.getContainingDeclaration() = withContainingElements.filterIsInstance().firstOrNull() diff --git a/uast/uast-common/src/org/jetbrains/uast/declarations/UFile.kt b/uast/uast-common/src/org/jetbrains/uast/declarations/UFile.kt new file mode 100644 index 000000000000..a9cfb5367c2f --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/declarations/UFile.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.PsiFile +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a Uast file. + */ +interface UFile : UElement, UAnnotated { + /** + * Returns the original [PsiFile]. + */ + override val psi: PsiFile + + /** + * Returns the Java package name of this file. + * Returns an empty [String] for the default package. + */ + val packageName: String + + /** + * Returns the import statements for this file. + */ + val imports: List + + /** + * Returns the list of top-level classes declared in this file. + */ + val classes: List + + /** + * Returns the plugin for a language used in this file. + */ + val languagePlugin: UastLanguagePlugin + + /** + * Returns all comments in file. + */ + val allCommentsInFile: List + + override fun asLogString() = log("package = $packageName") + + override fun asRenderString() = buildString { + val packageName = this@UFile.packageName + if (packageName.isNotEmpty()) appendln("package $packageName").appendln() + + val imports = this@UFile.imports + if (imports.isNotEmpty()) { + imports.forEach { appendln(it.asRenderString()) } + appendln() + } + + classes.forEachIndexed { index, clazz -> + if (index > 0) appendln() + appendln(clazz.asRenderString()) + } + } + + /** + * [UFile] is a top-level element of the Uast hierarchy, thus the [uastParent] always returns null for it. + */ + override val uastParent: UElement? + get() = null + + override fun accept(visitor: UastVisitor) { + if (visitor.visitFile(this)) return + annotations.acceptList(visitor) + imports.acceptList(visitor) + classes.acceptList(visitor) + visitor.afterVisitFile(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitFile(this, data) +} + diff --git a/uast/uast-common/src/org/jetbrains/uast/declarations/UImportStatement.kt b/uast/uast-common/src/org/jetbrains/uast/declarations/UImportStatement.kt new file mode 100644 index 000000000000..8539e029fa8c --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/declarations/UImportStatement.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents an import statement. + */ +interface UImportStatement : UResolvable, UElement { + /** + * Returns true if the statement is an import-on-demand (star-import) statement. + */ + val isOnDemand: Boolean + + /** + * Returns the reference to the imported element. + */ + val importReference: UElement? + + override fun asLogString() = log("isOnDemand = $isOnDemand") + + override fun asRenderString() = "import " + (importReference?.asRenderString() ?: "") + + override fun accept(visitor: UastVisitor) { + if (visitor.visitImportStatement(this)) return + visitor.afterVisitImportStatement(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitImportStatement(this, data) +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/declarations/UMethod.kt b/uast/uast-common/src/org/jetbrains/uast/declarations/UMethod.kt new file mode 100644 index 000000000000..d1cb5125b5ca --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/declarations/UMethod.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.PsiAnnotationMethod +import com.intellij.psi.PsiMethod +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * A method visitor to be used in [UastVisitor]. + */ +interface UMethod : UDeclaration, PsiMethod { + override val psi: PsiMethod + + /** + * Returns the body expression (which can be also a [UBlockExpression]). + */ + val uastBody: UExpression? + + /** + * Returns the method parameters. + */ + val uastParameters: List + + /** + * Returns true, if the method overrides a method of a super class. + */ + val isOverride: Boolean + + @Deprecated("Use uastBody instead.", ReplaceWith("uastBody")) + override fun getBody() = psi.body + + override fun accept(visitor: UastVisitor) { + if (visitor.visitMethod(this)) return + annotations.acceptList(visitor) + uastParameters.acceptList(visitor) + uastBody?.accept(visitor) + visitor.afterVisitMethod(this) + } + + override fun asRenderString() = buildString { + if (annotations.isNotEmpty()) { + annotations.joinTo(buffer = this, separator = "\n", postfix = "\n", transform = UAnnotation::asRenderString) + } + + append(psi.renderModifiers()) + append("fun ").append(name) + + uastParameters.joinTo(this, prefix = "(", postfix = ")") { + it.name + ": " + it.type.canonicalText + } + + psi.returnType?.let { append(" : " + it.canonicalText) } + + val body = uastBody + append(when (body) { + is UBlockExpression -> " " + body.asRenderString() + else -> " = " + ((body ?: UastEmptyExpression).asRenderString()) + }) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitMethod(this, data) + + override fun asLogString() = log("name = $name") +} + +interface UAnnotationMethod : UMethod, PsiAnnotationMethod { + override val psi: PsiAnnotationMethod + + /** + * Returns the default value of this annotation method. + */ + val uastDefaultValue: UExpression? + + override fun getDefaultValue() = psi.defaultValue + + override fun accept(visitor: UastVisitor) { + if (visitor.visitMethod(this)) return + annotations.acceptList(visitor) + uastParameters.acceptList(visitor) + uastBody?.accept(visitor) + uastDefaultValue?.accept(visitor) + visitor.afterVisitMethod(this) + } + + override fun asLogString() = log("name = $name") +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/declarations/UVariable.kt b/uast/uast-common/src/org/jetbrains/uast/declarations/UVariable.kt new file mode 100644 index 000000000000..2fb65dc0cc6e --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/declarations/UVariable.kt @@ -0,0 +1,141 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.* +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * A variable wrapper to be used in [UastVisitor]. + */ +interface UVariable : UDeclaration, PsiVariable { + override val psi: PsiVariable + + /** + * Returns the variable initializer or the parameter default value, or null if the variable has not an initializer. + */ + val uastInitializer: UExpression? + + /** + * Returns variable type reference. + */ + val typeReference: UTypeReferenceExpression? + + override fun accept(visitor: UastVisitor) { + if (visitor.visitVariable(this)) return + visitContents(visitor) + visitor.afterVisitVariable(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitVariable(this, data) + + @Deprecated("Use uastInitializer instead.", ReplaceWith("uastInitializer")) + override fun getInitializer() = psi.initializer + + override fun asLogString() = log("name = $name") + + override fun asRenderString() = buildString { + append(psi.renderModifiers()) + append("var ").append(psi.name).append(": ").append(psi.type.getCanonicalText(false)) + uastInitializer?.let { initializer -> append(" = " + initializer.asRenderString()) } + } +} + +private fun UVariable.visitContents(visitor: UastVisitor) { + annotations.acceptList(visitor) + uastInitializer?.accept(visitor) +} + +interface UParameter : UVariable, PsiParameter { + override val psi: PsiParameter + + override fun asLogString() = log("name = $name") + + override fun accept(visitor: UastVisitor) { + if (visitor.visitParameter(this)) return + visitContents(visitor) + visitor.afterVisitParameter(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = visitor.visitParameter(this, data) +} + +interface UField : UVariable, PsiField { + override val psi: PsiField + + override fun asLogString() = log("name = $name") + + override fun accept(visitor: UastVisitor) { + if (visitor.visitField(this)) return + visitContents(visitor) + visitor.afterVisitField(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = visitor.visitField(this, data) +} + +interface ULocalVariable : UVariable, PsiLocalVariable { + override val psi: PsiLocalVariable + + override fun asLogString() = log("name = $name") + + override fun accept(visitor: UastVisitor) { + if (visitor.visitLocalVariable(this)) return + visitContents(visitor) + visitor.afterVisitLocalVariable(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = visitor.visitLocalVariable(this, data) +} + +interface UEnumConstant : UField, UCallExpression, PsiEnumConstant { + override val psi: PsiEnumConstant + + val initializingClass: UClass? + + override fun asLogString() = log("name = $name") + + override fun accept(visitor: UastVisitor) { + if (visitor.visitEnumConstant(this)) return + annotations.acceptList(visitor) + methodIdentifier?.accept(visitor) + classReference?.accept(visitor) + valueArguments.acceptList(visitor) + initializingClass?.accept(visitor) + visitor.afterVisitEnumConstant(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitEnumConstantExpression(this, data) + + override fun asRenderString() = buildString { + append(name ?: "") + if (valueArguments.isNotEmpty()) { + valueArguments.joinTo(this, prefix = "(", postfix = ")", transform = UExpression::asRenderString) + } + initializingClass?.let { + appendln(" {") + it.uastDeclarations.forEachIndexed { index, declaration -> + appendln(declaration.asRenderString().withMargin) + } + append("}") + } + } +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/evaluation/AbstractEvaluationState.kt b/uast/uast-common/src/org/jetbrains/uast/evaluation/AbstractEvaluationState.kt new file mode 100644 index 000000000000..99aeb3290b9c --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/evaluation/AbstractEvaluationState.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.evaluation + +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UVariable +import org.jetbrains.uast.values.UValue +import org.jetbrains.uast.values.UVariableValue + +abstract class AbstractEvaluationState(override val boundElement: UElement? = null) : UEvaluationState { + override fun assign(variable: UVariable, value: UValue, at: UElement): AbstractEvaluationState { + val variableValue = UVariableValue.create(variable, value) + val prevVariableValue = this[variable] + return if (prevVariableValue == variableValue) this + else DelegatingEvaluationState( + boundElement = at, + variableValue = variableValue, + baseState = this + ) + } + + override fun merge(otherState: UEvaluationState) = + if (this == otherState) this else MergingEvaluationState(this, otherState) + + override fun equals(other: Any?) = + other is UEvaluationState && variables == other.variables && variables.all { this[it] == other[it] } + + override fun hashCode(): Int { + var result = 31 + result = result * 19 + variables.hashCode() + result = result * 19 + variables.map { this[it].hashCode() }.sum() + return result + } + + override fun toString() = variables.joinToString(prefix = "[", postfix = "]", separator = ", ") { + "${it.psi.name} = ${this[it]}" + } +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/evaluation/AbstractEvaluatorExtension.kt b/uast/uast-common/src/org/jetbrains/uast/evaluation/AbstractEvaluatorExtension.kt new file mode 100644 index 000000000000..a3775e4ea5cb --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/evaluation/AbstractEvaluatorExtension.kt @@ -0,0 +1,112 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.evaluation + +import com.intellij.lang.Language +import com.intellij.psi.PsiMethod +import org.jetbrains.uast.* +import org.jetbrains.uast.values.UUndeterminedValue +import org.jetbrains.uast.values.UValue + +abstract class AbstractEvaluatorExtension(override val language: Language) : UEvaluatorExtension { + override fun evaluatePostfix( + operator: UastPostfixOperator, + operandValue: UValue, + state: UEvaluationState + ): UEvaluationInfo = UUndeterminedValue to state + + override fun evaluatePrefix( + operator: UastPrefixOperator, + operandValue: UValue, + state: UEvaluationState + ): UEvaluationInfo = UUndeterminedValue to state + + override fun evaluateBinary( + binaryExpression: UBinaryExpression, + leftValue: UValue, + rightValue: UValue, + state: UEvaluationState + ): UEvaluationInfo = UUndeterminedValue to state + + override fun evaluateQualified( + accessType: UastQualifiedExpressionAccessType, + receiverInfo: UEvaluationInfo, + selectorInfo: UEvaluationInfo + ): UEvaluationInfo = UUndeterminedValue to selectorInfo.state + + override fun evaluateMethodCall( + target: PsiMethod, + argumentValues: List, + state: UEvaluationState + ): UEvaluationInfo = UUndeterminedValue to state + + override fun evaluateVariable( + variable: UVariable, + state: UEvaluationState + ): UEvaluationInfo = UUndeterminedValue to state +} + +abstract class SimpleEvaluatorExtension : AbstractEvaluatorExtension(Language.ANY) { + override final fun evaluatePostfix(operator: UastPostfixOperator, operandValue: UValue, state: UEvaluationState): UEvaluationInfo { + val result = evaluatePostfix(operator, operandValue) + return if (result != UUndeterminedValue) + result.toConstant() to state + else + super.evaluatePostfix(operator, operandValue, state) + } + + open fun evaluatePostfix(operator: UastPostfixOperator, operandValue: UValue): Any? = UUndeterminedValue + + override final fun evaluatePrefix(operator: UastPrefixOperator, operandValue: UValue, state: UEvaluationState): UEvaluationInfo { + val result = evaluatePrefix(operator, operandValue) + return if (result != UUndeterminedValue) + result.toConstant() to state + else + super.evaluatePrefix(operator, operandValue, state) + } + + open fun evaluatePrefix(operator: UastPrefixOperator, operandValue: UValue): Any? = UUndeterminedValue + + override final fun evaluateBinary(binaryExpression: UBinaryExpression, leftValue: UValue, rightValue: UValue, state: UEvaluationState): UEvaluationInfo { + val result = evaluateBinary(binaryExpression, leftValue, rightValue) + return if (result != UUndeterminedValue) + result.toConstant() to state + else + super.evaluateBinary(binaryExpression, leftValue, rightValue, state) + } + + open fun evaluateBinary(binaryExpression: UBinaryExpression, leftValue: UValue, rightValue: UValue): Any? = UUndeterminedValue + + override final fun evaluateMethodCall(target: PsiMethod, argumentValues: List, state: UEvaluationState): UEvaluationInfo { + val result = evaluateMethodCall(target, argumentValues) + return if (result != UUndeterminedValue) + result.toConstant() to state + else + super.evaluateMethodCall(target, argumentValues, state) + } + + open fun evaluateMethodCall(target: PsiMethod, argumentValues: List): Any? = UUndeterminedValue + + override final fun evaluateVariable(variable: UVariable, state: UEvaluationState): UEvaluationInfo { + val result = evaluateVariable(variable) + return if (result != UUndeterminedValue) + result.toConstant() to state + else + super.evaluateVariable(variable, state) + } + + open fun evaluateVariable(variable: UVariable): Any? = UUndeterminedValue +} diff --git a/uast/uast-common/src/org/jetbrains/uast/evaluation/DelegatingEvaluationState.kt b/uast/uast-common/src/org/jetbrains/uast/evaluation/DelegatingEvaluationState.kt new file mode 100644 index 000000000000..fbdf117768cf --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/evaluation/DelegatingEvaluationState.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.evaluation + +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UVariable +import org.jetbrains.uast.values.UVariableValue + +class DelegatingEvaluationState( + boundElement: UElement, + private val variableValue: UVariableValue, + private val baseState: UEvaluationState +) : AbstractEvaluationState(boundElement) { + + override val variables = baseState.variables + variableValue.variable + + override fun get(variable: UVariable) = + if (variable == variableValue.variable) variableValue else baseState[variable] +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/evaluation/EmptyEvaluationState.kt b/uast/uast-common/src/org/jetbrains/uast/evaluation/EmptyEvaluationState.kt new file mode 100644 index 000000000000..89417f3658ed --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/evaluation/EmptyEvaluationState.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.evaluation + +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UVariable +import org.jetbrains.uast.values.UUndeterminedValue + +class EmptyEvaluationState(boundElement: UElement) : AbstractEvaluationState(boundElement) { + override val variables: Set + get() = emptySet() + + override fun get(variable: UVariable) = UUndeterminedValue +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/evaluation/MapBasedEvaluationContext.kt b/uast/uast-common/src/org/jetbrains/uast/evaluation/MapBasedEvaluationContext.kt new file mode 100644 index 000000000000..3a7f65bc23c8 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/evaluation/MapBasedEvaluationContext.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.evaluation + +import org.jetbrains.uast.* +import org.jetbrains.uast.values.UUndeterminedValue +import org.jetbrains.uast.visitor.UastVisitor +import java.lang.ref.SoftReference +import java.util.* + +class MapBasedEvaluationContext( + override val uastContext: UastContext, + override val extensions: List +) : UEvaluationContext { + private val evaluators = WeakHashMap>() + + override fun analyzeAll(file: UFile, state: UEvaluationState): UEvaluationContext { + file.accept(object: UastVisitor { + override fun visitElement(node: UElement) = false + + override fun visitMethod(node: UMethod): Boolean { + analyze(node, state) + return true + } + + override fun visitVariable(node: UVariable): Boolean { + if (node is UField) { + analyze(node, state) + return true + } + else return false + } + }) + return this + } + + private fun getOrCreateEvaluator(declaration: UDeclaration, state: UEvaluationState? = null) = + evaluators[declaration]?.get() ?: createEvaluator(uastContext, extensions).apply { + when (declaration) { + is UMethod -> this.analyze(declaration, state ?: declaration.createEmptyState()) + is UField -> this.analyze(declaration, state ?: declaration.createEmptyState()) + } + evaluators[declaration] = SoftReference(this) + } + + override fun analyze(declaration: UDeclaration, state: UEvaluationState) = getOrCreateEvaluator(declaration, state) + + override fun getEvaluator(declaration: UDeclaration) = getOrCreateEvaluator(declaration) + + private fun getEvaluator(expression: UExpression): UEvaluator? { + var containingElement = expression.uastParent + while (containingElement != null) { + if (containingElement is UDeclaration) { + val evaluator = evaluators[containingElement]?.get() + if (evaluator != null) { + return evaluator + } + } + containingElement = containingElement.uastParent + } + return null + } + + override fun valueOf(expression: UExpression) = + getEvaluator(expression)?.evaluate(expression) ?: UUndeterminedValue +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/evaluation/MergingEvaluationState.kt b/uast/uast-common/src/org/jetbrains/uast/evaluation/MergingEvaluationState.kt new file mode 100644 index 000000000000..71da9b46cf2e --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/evaluation/MergingEvaluationState.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.evaluation + +import org.jetbrains.uast.UVariable + +class MergingEvaluationState( + private val first: UEvaluationState, + private val second: UEvaluationState +) : AbstractEvaluationState() { + + override val variables = first.variables + second.variables + + override fun get(variable: UVariable) = first[variable].merge(second[variable]) +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/evaluation/TreeBasedEvaluator.kt b/uast/uast-common/src/org/jetbrains/uast/evaluation/TreeBasedEvaluator.kt new file mode 100644 index 000000000000..e7682a6abb34 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/evaluation/TreeBasedEvaluator.kt @@ -0,0 +1,652 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.evaluation + +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiModifier +import com.intellij.psi.PsiType +import com.intellij.psi.PsiVariable +import org.jetbrains.uast.* +import org.jetbrains.uast.values.* +import org.jetbrains.uast.values.UNothingValue.JumpKind.BREAK +import org.jetbrains.uast.values.UNothingValue.JumpKind.CONTINUE +import org.jetbrains.uast.visitor.UastTypedVisitor + +class TreeBasedEvaluator( + override val context: UastContext, + val extensions: List +) : UastTypedVisitor, UEvaluator { + + override fun getDependents(dependency: UDependency): Set { + return resultCache.values.map { it.value }.filter { dependency in it.dependencies }.toSet() + } + + private val inputStateCache = mutableMapOf() + + private val resultCache = mutableMapOf() + + override fun visitElement(node: UElement, data: UEvaluationState): UEvaluationInfo { + return UEvaluationInfo(UUndeterminedValue, data).apply { + if (node is UExpression) { + this storeResultFor node + } + } + } + + override fun analyze(method: UMethod, state: UEvaluationState) { + method.uastBody?.accept(this, state) + } + + override fun analyze(field: UField, state: UEvaluationState) { + field.uastInitializer?.accept(this, state) + } + + override fun evaluate(expression: UExpression, state: UEvaluationState?): UValue { + if (state == null) { + val result = resultCache[expression] + if (result != null) return result.value + } + val inputState = state ?: inputStateCache[expression] ?: expression.createEmptyState() + return expression.accept(this, inputState).value + } + + // ----------------------- // + + private infix fun UValue.to(state: UEvaluationState) = UEvaluationInfo(this, state) + + private infix fun UEvaluationInfo.storeResultFor(expression: UExpression) = apply { + resultCache[expression] = this + } + + override fun visitLiteralExpression(node: ULiteralExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + val value = node.value + return value.toConstant(node) to data storeResultFor node + } + + override fun visitClassLiteralExpression(node: UClassLiteralExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + return (node.type?.let { value -> UClassConstant(value, node) } ?: UUndeterminedValue) to data storeResultFor node + } + + override fun visitReturnExpression(node: UReturnExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + val argument = node.returnExpression + return UValue.UNREACHABLE to (argument?.accept(this, data)?.state ?: data) storeResultFor node + } + + override fun visitBreakExpression(node: UBreakExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + return UNothingValue(node) to data storeResultFor node + } + override fun visitContinueExpression(node: UContinueExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + return UNothingValue(node) to data storeResultFor node + } + + override fun visitThrowExpression(node: UThrowExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + return UValue.UNREACHABLE to data storeResultFor node + } + // ----------------------- // + + override fun visitSimpleNameReferenceExpression( + node: USimpleNameReferenceExpression, + data: UEvaluationState + ): UEvaluationInfo { + inputStateCache[node] = data + val resolvedElement = node.resolveToUElement() + return when (resolvedElement) { + is UEnumConstant -> UEnumEntryValueConstant(resolvedElement, node) + is UField -> if (resolvedElement.hasModifierProperty(PsiModifier.FINAL)) { + data[resolvedElement].ifUndetermined { + val helper = JavaPsiFacade.getInstance(resolvedElement.project).constantEvaluationHelper + val evaluated = helper.computeConstantExpression(resolvedElement.initializer) + evaluated?.toConstant() ?: UUndeterminedValue + } + } + else { + return super.visitSimpleNameReferenceExpression(node, data) + } + is UVariable -> data[resolvedElement].ifUndetermined { + node.evaluateViaExtensions { evaluateVariable(resolvedElement, data) }?.value ?: UUndeterminedValue + } + else -> return super.visitSimpleNameReferenceExpression(node, data) + } to data storeResultFor node + } + + override fun visitReferenceExpression( + node: UReferenceExpression, + data: UEvaluationState + ): UEvaluationInfo { + inputStateCache[node] = data + return UCallResultValue(node, emptyList()) to data storeResultFor node + } + + // ----------------------- // + + private fun UExpression.assign( + valueInfo: UEvaluationInfo, + operator: UastBinaryOperator.AssignOperator = UastBinaryOperator.ASSIGN + ): UEvaluationInfo { + this.accept(this@TreeBasedEvaluator, valueInfo.state) + if (this is UResolvable) { + val resolvedElement = resolve() + if (resolvedElement is PsiVariable) { + val variable = context.getVariable(resolvedElement) + val currentValue = valueInfo.state[variable] + val result = when (operator) { + UastBinaryOperator.ASSIGN -> valueInfo.value + UastBinaryOperator.PLUS_ASSIGN -> currentValue + valueInfo.value + UastBinaryOperator.MINUS_ASSIGN -> currentValue - valueInfo.value + UastBinaryOperator.MULTIPLY_ASSIGN -> currentValue * valueInfo.value + UastBinaryOperator.DIVIDE_ASSIGN -> currentValue / valueInfo.value + UastBinaryOperator.REMAINDER_ASSIGN -> currentValue % valueInfo.value + UastBinaryOperator.AND_ASSIGN -> currentValue bitwiseAnd valueInfo.value + UastBinaryOperator.OR_ASSIGN -> currentValue bitwiseOr valueInfo.value + UastBinaryOperator.XOR_ASSIGN -> currentValue bitwiseXor valueInfo.value + UastBinaryOperator.SHIFT_LEFT_ASSIGN -> currentValue shl valueInfo.value + UastBinaryOperator.SHIFT_RIGHT_ASSIGN -> currentValue shr valueInfo.value + UastBinaryOperator.UNSIGNED_SHIFT_RIGHT_ASSIGN -> currentValue ushr valueInfo.value + else -> UUndeterminedValue + } + return result to valueInfo.state.assign(variable, result, this) + } + } + return UUndeterminedValue to valueInfo.state + } + + private fun UExpression.assign( + operator: UastBinaryOperator.AssignOperator, + value: UExpression, + data: UEvaluationState + ) = assign(value.accept(this@TreeBasedEvaluator, data), operator) + + override fun visitPrefixExpression(node: UPrefixExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + val operandInfo = node.operand.accept(this, data) + val operandValue = operandInfo.value + if (!operandValue.reachable) return operandInfo storeResultFor node + return when (node.operator) { + UastPrefixOperator.UNARY_PLUS -> operandValue + UastPrefixOperator.UNARY_MINUS -> -operandValue + UastPrefixOperator.LOGICAL_NOT -> !operandValue + UastPrefixOperator.INC -> { + val resultValue = operandValue.inc() + val newState = node.operand.assign(resultValue to operandInfo.state).state + return resultValue to newState storeResultFor node + } + UastPrefixOperator.DEC -> { + val resultValue = operandValue.dec() + val newState = node.operand.assign(resultValue to operandInfo.state).state + return resultValue to newState storeResultFor node + } + else -> { + return node.evaluateViaExtensions { evaluatePrefix(node.operator, operandValue, operandInfo.state) } + ?: UUndeterminedValue to operandInfo.state storeResultFor node + } + } to operandInfo.state storeResultFor node + } + + inline fun UElement.evaluateViaExtensions(block: UEvaluatorExtension.() -> UEvaluationInfo): UEvaluationInfo? { + for (ext in extensions) { + val extResult = ext.block() + if (extResult.value != UUndeterminedValue) return extResult + } + languageExtension()?.block()?.let { if (it.value != UUndeterminedValue) return it } + return null + } + + override fun visitPostfixExpression(node: UPostfixExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + val operandInfo = node.operand.accept(this, data) + val operandValue = operandInfo.value + if (!operandValue.reachable) return operandInfo storeResultFor node + return when (node.operator) { + UastPostfixOperator.INC -> { + operandValue to node.operand.assign(operandValue.inc() to operandInfo.state).state + } + UastPostfixOperator.DEC -> { + operandValue to node.operand.assign(operandValue.dec() to operandInfo.state).state + } + else -> { + return node.evaluateViaExtensions { evaluatePostfix(node.operator, operandValue, operandInfo.state) } + ?: UUndeterminedValue to operandInfo.state storeResultFor node + } + } storeResultFor node + } + + private fun UastBinaryOperator.evaluate(left: UValue, right: UValue): UValue? = + when (this) { + UastBinaryOperator.PLUS -> left + right + UastBinaryOperator.MINUS -> left - right + UastBinaryOperator.MULTIPLY -> left * right + UastBinaryOperator.DIV -> left / right + UastBinaryOperator.MOD -> left % right + UastBinaryOperator.EQUALS -> left valueEquals right + UastBinaryOperator.NOT_EQUALS -> left valueNotEquals right + UastBinaryOperator.IDENTITY_EQUALS -> left identityEquals right + UastBinaryOperator.IDENTITY_NOT_EQUALS -> left identityNotEquals right + UastBinaryOperator.GREATER -> left greater right + UastBinaryOperator.LESS -> left less right + UastBinaryOperator.GREATER_OR_EQUALS -> left greaterOrEquals right + UastBinaryOperator.LESS_OR_EQUALS -> left lessOrEquals right + UastBinaryOperator.LOGICAL_AND -> left and right + UastBinaryOperator.LOGICAL_OR -> left or right + UastBinaryOperator.BITWISE_AND -> left bitwiseAnd right + UastBinaryOperator.BITWISE_OR -> left bitwiseOr right + UastBinaryOperator.BITWISE_XOR -> left bitwiseXor right + UastBinaryOperator.SHIFT_LEFT -> left shl right + UastBinaryOperator.SHIFT_RIGHT -> left shr right + UastBinaryOperator.UNSIGNED_SHIFT_RIGHT -> left ushr right + else -> null + } + + override fun visitBinaryExpression(node: UBinaryExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + val operator = node.operator + + if (operator is UastBinaryOperator.AssignOperator) { + return node.leftOperand.assign(operator, node.rightOperand, data) storeResultFor node + } + + val leftInfo = node.leftOperand.accept(this, data) + if (!leftInfo.reachable) { + return leftInfo storeResultFor node + } + + val rightInfo = node.rightOperand.accept(this, leftInfo.state) + + operator.evaluate(leftInfo.value, rightInfo.value)?.let { + return it to rightInfo.state storeResultFor node + } + + return node.evaluateViaExtensions { evaluateBinary(node, leftInfo.value, rightInfo.value, rightInfo.state) } + ?: UUndeterminedValue to rightInfo.state storeResultFor node + } + + override fun visitPolyadicExpression(node: UPolyadicExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + val operator = node.operator + + val infos = node.operands.map { + it.accept(this, data).apply { + if (!reachable) { + return this storeResultFor node + } + } + } + + val lastInfo = infos.last() + val firstValue = infos.first().value + val restInfos = infos.drop(1) + + return restInfos.fold(firstValue) { accumulator, info -> + operator.evaluate(accumulator, info.value) ?: return UUndeterminedValue to info.state storeResultFor node + } to lastInfo.state storeResultFor node + } + + private fun evaluateTypeCast(operandInfo: UEvaluationInfo, type: PsiType): UEvaluationInfo { + val constant = operandInfo.value.toConstant() ?: return UUndeterminedValue to operandInfo.state + val resultConstant = when (type) { + PsiType.BOOLEAN -> { + constant as? UBooleanConstant + } + PsiType.CHAR -> when (constant) { + // Join the following three in UNumericConstant + // when https://youtrack.jetbrains.com/issue/KT-14868 is fixed + is UIntConstant -> UCharConstant(constant.value.toChar()) + is ULongConstant -> UCharConstant(constant.value.toChar()) + is UFloatConstant -> UCharConstant(constant.value.toChar()) + + is UCharConstant -> constant + else -> null + } + PsiType.LONG -> { + (constant as? UNumericConstant)?.value?.toLong()?.let { value -> ULongConstant(value) } + } + PsiType.BYTE, PsiType.SHORT, PsiType.INT -> { + (constant as? UNumericConstant)?.value?.toInt()?.let { UIntConstant(it, type) } + } + PsiType.FLOAT, PsiType.DOUBLE -> { + (constant as? UNumericConstant)?.value?.toDouble()?.let { UFloatConstant.create(it, type) } + } + else -> when (type.name) { + "java.lang.String" -> UStringConstant(constant.asString()) + else -> null + } + } ?: return UUndeterminedValue to operandInfo.state + return when (operandInfo.value) { + resultConstant -> return operandInfo + is UConstant -> resultConstant + is UDependentValue -> UDependentValue.create(resultConstant, operandInfo.value.dependencies) + else -> UUndeterminedValue + } to operandInfo.state + } + + private fun evaluateTypeCheck(operandInfo: UEvaluationInfo, type: PsiType): UEvaluationInfo { + val constant = operandInfo.value.toConstant() ?: return UUndeterminedValue to operandInfo.state + val valid = when (type) { + PsiType.BOOLEAN -> constant is UBooleanConstant + PsiType.LONG -> constant is ULongConstant + PsiType.BYTE, PsiType.SHORT, PsiType.INT, PsiType.CHAR -> constant is UIntConstant + PsiType.FLOAT, PsiType.DOUBLE -> constant is UFloatConstant + else -> when (type.name) { + "java.lang.String" -> constant is UStringConstant + else -> false + } + } + return UBooleanConstant.valueOf(valid) to operandInfo.state + } + + override fun visitBinaryExpressionWithType( + node: UBinaryExpressionWithType, data: UEvaluationState + ): UEvaluationInfo { + inputStateCache[node] = data + val operandInfo = node.operand.accept(this, data) + if (!operandInfo.reachable || operandInfo.value == UUndeterminedValue) { + return operandInfo storeResultFor node + } + return when (node.operationKind) { + UastBinaryExpressionWithTypeKind.TYPE_CAST -> evaluateTypeCast(operandInfo, node.type) + UastBinaryExpressionWithTypeKind.INSTANCE_CHECK -> evaluateTypeCheck(operandInfo, node.type) + else -> UUndeterminedValue to operandInfo.state + } storeResultFor node + } + + override fun visitParenthesizedExpression(node: UParenthesizedExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + return node.expression.accept(this, data) storeResultFor node + } + + override fun visitLabeledExpression(node: ULabeledExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + return node.expression.accept(this, data) storeResultFor node + } + + override fun visitCallExpression(node: UCallExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + + var currentInfo = UUndeterminedValue to data + currentInfo = node.receiver?.accept(this, currentInfo.state) ?: currentInfo + if (!currentInfo.reachable) return currentInfo storeResultFor node + val argumentValues = mutableListOf() + for (valueArgument in node.valueArguments) { + currentInfo = valueArgument.accept(this, currentInfo.state) + if (!currentInfo.reachable) return currentInfo storeResultFor node + argumentValues.add(currentInfo.value) + } + + return (node.evaluateViaExtensions { + node.resolve()?.let { method -> evaluateMethodCall(method, argumentValues, currentInfo.state) } + ?: UUndeterminedValue to currentInfo.state + } ?: UCallResultValue(node, argumentValues) to currentInfo.state) storeResultFor node + } + + override fun visitQualifiedReferenceExpression( + node: UQualifiedReferenceExpression, + data: UEvaluationState + ): UEvaluationInfo { + inputStateCache[node] = data + + var currentInfo = UUndeterminedValue to data + currentInfo = node.receiver.accept(this, currentInfo.state) + if (!currentInfo.reachable) return currentInfo storeResultFor node + + val selectorInfo = node.selector.accept(this, currentInfo.state) + return when (node.accessType) { + UastQualifiedExpressionAccessType.SIMPLE -> { + selectorInfo + } + else -> { + return node.evaluateViaExtensions { evaluateQualified(node.accessType, currentInfo, selectorInfo) } + ?: UUndeterminedValue to selectorInfo.state storeResultFor node + } + } storeResultFor node + } + + override fun visitDeclarationsExpression( + node: UDeclarationsExpression, + data: UEvaluationState + ): UEvaluationInfo { + inputStateCache[node] = data + var currentInfo = UUndeterminedValue to data + for (variable in node.declarations) { + currentInfo = variable.accept(this, currentInfo.state) + if (!currentInfo.reachable) return currentInfo storeResultFor node + } + return currentInfo storeResultFor node + } + + override fun visitVariable(node: UVariable, data: UEvaluationState): UEvaluationInfo { + val initializer = node.uastInitializer + val initializerInfo = initializer?.accept(this, data) ?: UUndeterminedValue to data + if (!initializerInfo.reachable) return initializerInfo + return UUndeterminedValue to initializerInfo.state.assign(node, initializerInfo.value, node) + } + + // ----------------------- // + + override fun visitBlockExpression(node: UBlockExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + var currentInfo = UUndeterminedValue to data + for (expression in node.expressions) { + currentInfo = expression.accept(this, currentInfo.state) + if (!currentInfo.reachable) return currentInfo storeResultFor node + } + return currentInfo storeResultFor node + } + + override fun visitIfExpression(node: UIfExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + val conditionInfo = node.condition.accept(this, data) + if (!conditionInfo.reachable) return conditionInfo storeResultFor node + + val thenInfo = node.thenExpression?.accept(this, conditionInfo.state) + val elseInfo = node.elseExpression?.accept(this, conditionInfo.state) + val conditionValue = conditionInfo.value + val defaultInfo = UUndeterminedValue to conditionInfo.state + val constantConditionValue = conditionValue.toConstant() + + return when (constantConditionValue) { + is UBooleanConstant -> { + if (constantConditionValue.value) thenInfo ?: defaultInfo + else elseInfo ?: defaultInfo + } + else -> when { + thenInfo == null -> elseInfo?.merge(defaultInfo) ?: defaultInfo + elseInfo == null -> thenInfo.merge(defaultInfo) + else -> thenInfo.merge(elseInfo) + } + } storeResultFor node + } + + override fun visitSwitchExpression(node: USwitchExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + val subjectInfo = node.expression?.accept(this, data) ?: UUndeterminedValue to data + if (!subjectInfo.reachable) return subjectInfo storeResultFor node + + var resultInfo: UEvaluationInfo? = null + var clauseInfo = subjectInfo + var fallThroughCondition: UValue = UBooleanConstant.False + + fun List.evaluateAndFold(): UValue = + this.map { + clauseInfo = it.accept(this@TreeBasedEvaluator, clauseInfo.state) + (clauseInfo.value valueEquals subjectInfo.value).toConstant() as? UValueBase ?: UUndeterminedValue + }.fold(UBooleanConstant.False) { previous: UValue, next -> previous or next } + + clausesLoop@ for (expression in node.body.expressions) { + val switchClauseWithBody = expression as USwitchClauseExpressionWithBody + val caseCondition = switchClauseWithBody.caseValues.evaluateAndFold().or(fallThroughCondition) + + if (caseCondition != UBooleanConstant.False) { + for (bodyExpression in switchClauseWithBody.body.expressions) { + clauseInfo = bodyExpression.accept(this, clauseInfo.state) + if (!clauseInfo.reachable) break + } + val clauseValue = clauseInfo.value + if (clauseValue is UNothingValue && clauseValue.containingLoopOrSwitch == node) { + // break from switch + resultInfo = resultInfo?.merge(clauseInfo) ?: clauseInfo + if (caseCondition == UBooleanConstant.True) break@clausesLoop + clauseInfo = subjectInfo + fallThroughCondition = UBooleanConstant.False + } + // TODO: jump out + else { + fallThroughCondition = caseCondition + clauseInfo = clauseInfo.merge(subjectInfo) + } + } + } + + resultInfo = resultInfo ?: subjectInfo + val resultValue = resultInfo.value + if (resultValue is UNothingValue && resultValue.containingLoopOrSwitch == node) { + resultInfo = resultInfo.copy(UUndeterminedValue) + } + return resultInfo storeResultFor node + } + + private fun evaluateLoop( + loop: ULoopExpression, + inputState: UEvaluationState, + condition: UExpression? = null, + infinite: Boolean = false, + update: UExpression? = null + ): UEvaluationInfo { + + fun evaluateCondition(inputState: UEvaluationState): UEvaluationInfo = + condition?.accept(this, inputState) + ?: (if (infinite) UBooleanConstant.True else UUndeterminedValue) to inputState + + var resultInfo = UUndeterminedValue to inputState + do { + val previousInfo = resultInfo + resultInfo = evaluateCondition(resultInfo.state) + val conditionConstant = resultInfo.value.toConstant() + if (conditionConstant == UBooleanConstant.False) { + return resultInfo.copy(UUndeterminedValue) storeResultFor loop + } + val bodyInfo = loop.body.accept(this, resultInfo.state) + val bodyValue = bodyInfo.value + if (bodyValue is UNothingValue) { + if (bodyValue.kind == BREAK && bodyValue.containingLoopOrSwitch == loop) { + return if (conditionConstant == UBooleanConstant.True) { + bodyInfo.copy(UUndeterminedValue) + } + else { + bodyInfo.copy(UUndeterminedValue).merge(previousInfo) + } storeResultFor loop + } + else if (bodyValue.kind == CONTINUE && bodyValue.containingLoopOrSwitch == loop) { + val updateInfo = update?.accept(this, bodyInfo.state) ?: bodyInfo + resultInfo = updateInfo.copy(UUndeterminedValue).merge(previousInfo) + } + else { + return if (conditionConstant == UBooleanConstant.True) { + bodyInfo + } + else { + resultInfo.copy(UUndeterminedValue) + } storeResultFor loop + } + } + else { + val updateInfo = update?.accept(this, bodyInfo.state) ?: bodyInfo + resultInfo = updateInfo.merge(previousInfo) + } + } while (previousInfo != resultInfo) + return resultInfo.copy(UUndeterminedValue) storeResultFor loop + } + + override fun visitForEachExpression(node: UForEachExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + val iterableInfo = node.iteratedValue.accept(this, data) + return evaluateLoop(node, iterableInfo.state) + } + + override fun visitForExpression(node: UForExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + val initialState = node.declaration?.accept(this, data)?.state ?: data + return evaluateLoop(node, initialState, node.condition, node.condition == null, node.update) + } + + override fun visitWhileExpression(node: UWhileExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + return evaluateLoop(node, data, node.condition) + } + + override fun visitDoWhileExpression(node: UDoWhileExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + val bodyInfo = node.body.accept(this, data) + return evaluateLoop(node, bodyInfo.state, node.condition) + } + + override fun visitTryExpression(node: UTryExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + val tryInfo = node.tryClause.accept(this, data) + val mergedTryInfo = tryInfo.merge(UUndeterminedValue to data) + val catchInfoList = node.catchClauses.map { it.accept(this, mergedTryInfo.state) } + val mergedTryCatchInfo = catchInfoList.fold(mergedTryInfo, UEvaluationInfo::merge) + val finallyInfo = node.finallyClause?.accept(this, mergedTryCatchInfo.state) ?: mergedTryCatchInfo + return finallyInfo storeResultFor node + } + + // ----------------------- // + + override fun visitObjectLiteralExpression(node: UObjectLiteralExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + val objectInfo = node.declaration.accept(this, data) + val resultState = data.merge(objectInfo.state) + return UUndeterminedValue to resultState storeResultFor node + } + + override fun visitLambdaExpression(node: ULambdaExpression, data: UEvaluationState): UEvaluationInfo { + inputStateCache[node] = data + val lambdaInfo = node.body.accept(this, data) + val resultState = data.merge(lambdaInfo.state) + return UUndeterminedValue to resultState storeResultFor node + } + + override fun visitClass(node: UClass, data: UEvaluationState): UEvaluationInfo { + // fields / initializers / nested classes? + var resultState = data + for (method in node.methods) { + resultState = resultState.merge(method.accept(this, resultState).state) + } + return UUndeterminedValue to resultState + } + + override fun visitMethod(node: UMethod, data: UEvaluationState): UEvaluationInfo { + return UUndeterminedValue to (node.uastBody?.accept(this, data)?.state ?: data) + } +} + +fun Any?.toConstant(node: ULiteralExpression? = null) = when(this) { + null -> UNullConstant + is Float -> UFloatConstant.create(this.toDouble(), UNumericType.FLOAT, node) + is Double -> UFloatConstant.create(this, UNumericType.DOUBLE, node) + is Long -> ULongConstant(this, node) + is Int -> UIntConstant(this, UNumericType.INT, node) + is Short -> UIntConstant(this.toInt(), UNumericType.SHORT, node) + is Byte -> UIntConstant(this.toInt(), UNumericType.BYTE, node) + is Char -> UCharConstant(this, node) + is Boolean -> UBooleanConstant.valueOf(this) + is String -> UStringConstant(this, node) + else -> UUndeterminedValue +} diff --git a/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluationContext.kt b/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluationContext.kt new file mode 100644 index 000000000000..daad664fcd0f --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluationContext.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.evaluation + +import com.intellij.openapi.util.Key +import org.jetbrains.uast.* +import org.jetbrains.uast.values.UValue +import java.lang.ref.SoftReference + +interface UEvaluationContext { + val uastContext: UastContext + + val extensions: List + + fun analyzeAll(file: UFile, state: UEvaluationState = file.createEmptyState()): UEvaluationContext + + fun analyze(declaration: UDeclaration, state: UEvaluationState = declaration.createEmptyState()): UEvaluator + + fun valueOf(expression: UExpression): UValue + + fun getEvaluator(declaration: UDeclaration): UEvaluator +} + +fun UFile.analyzeAll(context: UastContext = getUastContext(), extensions: List = emptyList()): UEvaluationContext = + MapBasedEvaluationContext(context, extensions).analyzeAll(this) + +@JvmOverloads +fun UExpression.uValueOf(extensions: List = emptyList()): UValue? { + val declaration = getContainingDeclaration() ?: return null + val context = declaration.getEvaluationContextWithCaching(extensions) + context.analyze(declaration) + return context.valueOf(this) +} + +fun UExpression.uValueOf(vararg extensions: UEvaluatorExtension): UValue? = uValueOf(extensions.asList()) + +fun UDeclaration.getEvaluationContextWithCaching(extensions: List = emptyList()): UEvaluationContext { + return containingFile?.let { file -> + val cachedContext = file.getUserData(EVALUATION_CONTEXT_KEY)?.get() + if (cachedContext != null && cachedContext.extensions == extensions) + cachedContext + else + MapBasedEvaluationContext(getUastContext(), extensions).apply { + file.putUserData(EVALUATION_CONTEXT_KEY, SoftReference(this)) + } + + } ?: MapBasedEvaluationContext(getUastContext(), extensions) +} + +val EVALUATION_CONTEXT_KEY = Key>("uast.EvaluationContext") diff --git a/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluationInfo.kt b/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluationInfo.kt new file mode 100644 index 000000000000..f2b73e0477ca --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluationInfo.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.evaluation + +import org.jetbrains.uast.values.UValue + +data class UEvaluationInfo(val value: UValue, val state: UEvaluationState) { + fun merge(otherInfo: UEvaluationInfo): UEvaluationInfo { + // info with 'UNothingValue' is just ignored, if other is not UNothingValue + if (!reachable && otherInfo.reachable) return otherInfo + if (!otherInfo.reachable && reachable) return this + // Regular merge + val mergedValue = value.merge(otherInfo.value) + val mergedState = state.merge(otherInfo.state) + return UEvaluationInfo(mergedValue, mergedState) + } + + fun copy(value: UValue) = if (value != this.value) UEvaluationInfo(value, state) else this + + val reachable: Boolean + get() = value.reachable +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluationState.kt b/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluationState.kt new file mode 100644 index 000000000000..e8e90ad959d1 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluationState.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.evaluation + +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UVariable +import org.jetbrains.uast.values.UValue + +// Role: stores current values for all variables (and may be something else) +// Immutable +interface UEvaluationState { + val boundElement: UElement? + + val variables: Set + + operator fun get(variable: UVariable): UValue + + // Creates new evaluation state with state[variable] = value and boundElement = at + fun assign(variable: UVariable, value: UValue, at: UElement): UEvaluationState + + // Merged two states + fun merge(otherState: UEvaluationState): UEvaluationState +} + +fun UElement.createEmptyState(): UEvaluationState = EmptyEvaluationState(this) \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluator.kt b/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluator.kt new file mode 100644 index 000000000000..4b2acb2e87d6 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluator.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.evaluation + +import com.intellij.openapi.extensions.Extensions +import com.intellij.psi.PsiElement +import org.jetbrains.uast.* +import org.jetbrains.uast.values.UDependency +import org.jetbrains.uast.values.UValue + +// Role: at the current state, evaluate expression(s) +interface UEvaluator { + + val context: UastContext + + val languageExtensions: List + get() { + val rootArea = Extensions.getRootArea() + if (!rootArea.hasExtensionPoint(UEvaluatorExtension.EXTENSION_POINT_NAME.name)) return listOf() + return rootArea.getExtensionPoint(UEvaluatorExtension.EXTENSION_POINT_NAME).extensions.toList() + } + + fun PsiElement.languageExtension() = languageExtensions.firstOrNull { it.language == language } + + fun UElement.languageExtension() = psi?.languageExtension() + + fun analyze(method: UMethod, state: UEvaluationState = method.createEmptyState()) + + fun analyze(field: UField, state: UEvaluationState = field.createEmptyState()) + + fun evaluate(expression: UExpression, state: UEvaluationState? = null): UValue + + fun getDependents(dependency: UDependency): Set +} + +fun createEvaluator(context: UastContext, extensions: List): UEvaluator = + TreeBasedEvaluator(context, extensions) diff --git a/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluatorExtension.kt b/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluatorExtension.kt new file mode 100644 index 000000000000..ffe3d3117da5 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluatorExtension.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.evaluation + +import com.intellij.lang.Language +import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.psi.PsiMethod +import org.jetbrains.uast.* +import org.jetbrains.uast.values.UValue + +interface UEvaluatorExtension { + + companion object { + val EXTENSION_POINT_NAME: ExtensionPointName = + ExtensionPointName.create("org.jetbrains.uast.evaluation.UEvaluatorExtension") + } + + infix fun UValue.to(state: UEvaluationState) = UEvaluationInfo(this, state) + + val language: Language + + fun evaluatePostfix( + operator: UastPostfixOperator, + operandValue: UValue, + state: UEvaluationState + ): UEvaluationInfo + + fun evaluatePrefix( + operator: UastPrefixOperator, + operandValue: UValue, + state: UEvaluationState + ): UEvaluationInfo + + fun evaluateBinary( + binaryExpression: UBinaryExpression, + leftValue: UValue, + rightValue: UValue, + state: UEvaluationState + ): UEvaluationInfo + + fun evaluateQualified( + accessType: UastQualifiedExpressionAccessType, + receiverInfo: UEvaluationInfo, + selectorInfo: UEvaluationInfo + ): UEvaluationInfo + + fun evaluateMethodCall( + target: PsiMethod, + argumentValues: List, + state: UEvaluationState + ): UEvaluationInfo + + fun evaluateVariable( + variable: UVariable, + state: UEvaluationState + ): UEvaluationInfo +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UArrayAccessExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UArrayAccessExpression.kt new file mode 100644 index 000000000000..25749ba3ac51 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UArrayAccessExpression.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents `receiver[index0, ..., indexN]` expression. + */ +interface UArrayAccessExpression : UExpression { + /** + * Returns the receiver expression. + */ + val receiver: UExpression + + /** + * Returns the list of index expressions. + */ + val indices: List + + override fun accept(visitor: UastVisitor) { + if (visitor.visitArrayAccessExpression(this)) return + annotations.acceptList(visitor) + receiver.accept(visitor) + indices.acceptList(visitor) + visitor.afterVisitArrayAccessExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitArrayAccessExpression(this, data) + + override fun asLogString() = log() + + override fun asRenderString() = receiver.asRenderString() + + indices.joinToString(prefix = "[", postfix = "]") { it.asRenderString() } +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UBinaryExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UBinaryExpression.kt new file mode 100644 index 000000000000..e0e59948de07 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UBinaryExpression.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.PsiMethod +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a binary expression (value1 op value2), eg. `2 + "A"`. + */ +interface UBinaryExpression : UPolyadicExpression { + /** + * Returns the left operand. + */ + val leftOperand: UExpression + + /** + * Returns the right operand. + */ + val rightOperand: UExpression + + /** + * Returns the operator identifier. + */ + val operatorIdentifier: UIdentifier? + + /** + * Resolve the operator method. + * + * @return the resolved method, or null if the method can't be resolved, or if the expression is not a method call. + */ + fun resolveOperator(): PsiMethod? + + override val operands: List + get() = listOf(leftOperand, rightOperand) + + override fun accept(visitor: UastVisitor) { + if (visitor.visitBinaryExpression(this)) return + annotations.acceptList(visitor) + leftOperand.accept(visitor) + rightOperand.accept(visitor) + visitor.afterVisitBinaryExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitBinaryExpression(this, data) + + override fun asLogString() = log("operator = $operator") +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UBinaryExpressionWithType.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UBinaryExpressionWithType.kt new file mode 100644 index 000000000000..6791715ddc09 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UBinaryExpressionWithType.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + + +import com.intellij.psi.PsiType +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a binary expression with type (value op type), e.g. ("A" instanceof String). + */ +interface UBinaryExpressionWithType : UExpression { + /** + * Returns the operand expression. + */ + val operand: UExpression + + /** + * Returns the operation kind. + */ + val operationKind: UastBinaryExpressionWithTypeKind + + /** + * Returns the type reference of this expression. + */ + val typeReference: UTypeReferenceExpression? + + /** + * Returns the type. + */ + val type: PsiType + + override fun asLogString() = log() + + override fun asRenderString() = "${operand.asRenderString()} ${operationKind.name} ${type.name}" + + override fun accept(visitor: UastVisitor) { + if (visitor.visitBinaryExpressionWithType(this)) return + annotations.acceptList(visitor) + operand.accept(visitor) + typeReference?.accept(visitor) + visitor.afterVisitBinaryExpressionWithType(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitBinaryExpressionWithType(this, data) +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UBlockExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UBlockExpression.kt new file mode 100644 index 000000000000..1dd672603c42 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UBlockExpression.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents the code block expression: `{ /* code */ }`. + */ +interface UBlockExpression : UExpression { + /** + * Returns the list of block expressions. + */ + val expressions: List + + override fun accept(visitor: UastVisitor) { + if (visitor.visitBlockExpression(this)) return + annotations.acceptList(visitor) + expressions.acceptList(visitor) + visitor.afterVisitBlockExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitBlockExpression(this, data) + + override fun asLogString() = log() + + override fun asRenderString() = buildString { + appendln("{") + expressions.forEach { appendln(it.asRenderString().withMargin) } + append("}") + } +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UBreakExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UBreakExpression.kt new file mode 100644 index 000000000000..f01003db9f53 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UBreakExpression.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast + +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a `break` expression. + */ +interface UBreakExpression : UJumpExpression { + + override fun accept(visitor: UastVisitor) { + if (visitor.visitBreakExpression(this)) return + annotations.acceptList(visitor) + visitor.afterVisitBreakExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitBreakExpression(this, data) + + override fun asLogString() = log("label = $label") + + override fun asRenderString() = label?.let { "break@$it" } ?: "break" +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UCallExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UCallExpression.kt new file mode 100644 index 000000000000..8c8505bc916f --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UCallExpression.kt @@ -0,0 +1,116 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + + +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiType +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a call expression (method/constructor call, array initializer). + */ +interface UCallExpression : UExpression, UResolvable { + /** + * Returns the call kind. + */ + val kind: UastCallKind + + /** + * Returns the called method name, or null if the call is not a method call. + * This property should return the actual resolved function name. + */ + val methodName: String? + + /** + * Returns the expression receiver. + * For example, for call `a.b.[c()]` the receiver is `a.b`. + */ + val receiver: UExpression? + + /** + * Returns the receiver type, or null if the call has not a receiver. + */ + val receiverType: PsiType? + + /** + * Returns the function reference expression if the call is a non-constructor method call, null otherwise. + */ + val methodIdentifier: UIdentifier? + + /** + * Returns the class reference if the call is a constructor call, null otherwise. + */ + val classReference: UReferenceExpression? + + /** + * Returns the value argument count. + * + * Retrieving the argument count could be faster than getting the [valueArguments.size], + * because there is no need to create actual [UExpression] instances. + */ + val valueArgumentCount: Int + + /** + * Returns the list of value arguments. + */ + val valueArguments: List + + /** + * Returns the type argument count. + */ + val typeArgumentCount: Int + + /** + * Returns the type arguments for the call. + */ + val typeArguments: List + + /** + * Returns the return type of the called function, or null if the call is not a function call. + */ + val returnType: PsiType? + + /** + * Resolve the called method. + * + * @return the [PsiMethod], or null if the method was not resolved. + * Note that the [PsiMethod] is an unwrapped [PsiMethod], not a [UMethod]. + */ + override fun resolve(): PsiMethod? + + override fun accept(visitor: UastVisitor) { + if (visitor.visitCallExpression(this)) return + annotations.acceptList(visitor) + methodIdentifier?.accept(visitor) + classReference?.accept(visitor) + valueArguments.acceptList(visitor) + visitor.afterVisitCallExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitCallExpression(this, data) + + override fun asLogString() = log("kind = $kind, argCount = $valueArgumentCount)") + + override fun asRenderString(): String { + val ref = classReference?.asRenderString() ?: methodName ?: methodIdentifier?.asRenderString() ?: "" + return ref + "(" + valueArguments.joinToString { it.asRenderString() } + ")" + } +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UCallableReferenceExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UCallableReferenceExpression.kt new file mode 100644 index 000000000000..cb8de92cb8e5 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UCallableReferenceExpression.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + + +import com.intellij.psi.PsiType +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a callable reference expression, e.g. `Clazz::methodName`. + */ +interface UCallableReferenceExpression : UReferenceExpression { + /** + * Returns the qualifier expression. + * Can be null if the [qualifierType] is known. + */ + val qualifierExpression: UExpression? + + /** + * Returns the qualifier type. + * Can be null if the qualifier is an expression. + */ + val qualifierType: PsiType? + + /** + * Returns the callable name. + */ + val callableName: String + + override fun accept(visitor: UastVisitor) { + if (visitor.visitCallableReferenceExpression(this)) return + annotations.acceptList(visitor) + qualifierExpression?.accept(visitor) + visitor.afterVisitCallableReferenceExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitCallableReferenceExpression(this, data) + + override fun asLogString() = log("name = $callableName") + + override fun asRenderString() = buildString { + qualifierExpression?.let { + append(it.asRenderString()) + } ?: qualifierType?.let { + append(it.name) + } + append("::") + append(callableName) + } +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UClassLiteralExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UClassLiteralExpression.kt new file mode 100644 index 000000000000..e7c6224744b6 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UClassLiteralExpression.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + + +import com.intellij.psi.PsiType +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents the class literal expression, e.g. `Clazz.class`. + */ +interface UClassLiteralExpression : UExpression { + override fun asLogString() = log() + + override fun asRenderString() = (type?.name) ?: "(${expression?.asRenderString() ?: ""})" + "::class" + + /** + * Returns the type referenced by this class literal, or null if the type can't be determined in a compile-time. + */ + val type: PsiType? + + /** + * Returns an expression for this class literal expression. + * Might be null if the [type] is specified. + */ + val expression: UExpression? + + override fun accept(visitor: UastVisitor) { + if (visitor.visitClassLiteralExpression(this)) return + annotations.acceptList(visitor) + expression?.accept(visitor) + visitor.afterVisitClassLiteralExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitClassLiteralExpression(this, data) +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UContinueExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UContinueExpression.kt new file mode 100644 index 000000000000..944faeadaae3 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UContinueExpression.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast + +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a `continue` expression. + */ +interface UContinueExpression : UJumpExpression { + + override fun accept(visitor: UastVisitor) { + if (visitor.visitContinueExpression(this)) return + annotations.acceptList(visitor) + visitor.afterVisitContinueExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitContinueExpression(this, data) + + override fun asLogString() = log("label = $label") + + override fun asRenderString() = label?.let { "continue@$it" } ?: "continue" +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UDeclarationsExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UDeclarationsExpression.kt new file mode 100644 index 000000000000..08768cea2413 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UDeclarationsExpression.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a list of declarations. + * Example in Java: `int a = 4, b = 3`. + */ +interface UDeclarationsExpression : UExpression { + /** + * Returns the list of declarations inside this [UDeclarationsExpression]. + */ + val declarations: List + + override fun accept(visitor: UastVisitor) { + if (visitor.visitDeclarationsExpression(this)) return + annotations.acceptList(visitor) + declarations.acceptList(visitor) + visitor.afterVisitDeclarationsExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitDeclarationsExpression(this, data) + + override fun asRenderString() = declarations.joinToString(LINE_SEPARATOR) { it.asRenderString() } + + override fun asLogString() = log() +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UExpressionList.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UExpressionList.kt new file mode 100644 index 000000000000..7d68ad02291a --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UExpressionList.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a generic list of expressions. + */ +interface UExpressionList : UExpression { + /** + * Returns the list of expressions. + */ + val expressions: List + + /** + * Returns the list kind. + */ + val kind: UastSpecialExpressionKind + + override fun accept(visitor: UastVisitor) { + if (visitor.visitExpressionList(this)) return + annotations.acceptList(visitor) + expressions.acceptList(visitor) + visitor.afterVisitExpressionList(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitExpressionList(this, data) + + fun firstOrNull(): UExpression? = expressions.firstOrNull() + + override fun asLogString() = log(kind.name) + + override fun asRenderString() = kind.name + " " + expressions.joinToString(" : ") { it.asRenderString() } +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UInstanceExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UInstanceExpression.kt new file mode 100644 index 000000000000..2a603da512d8 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UInstanceExpression.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast + +/** + * A common parent for "this" and "super" expressions. + */ +interface UInstanceExpression : UExpression, ULabeled, UResolvable \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UJumpExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UJumpExpression.kt new file mode 100644 index 000000000000..d5275811e4e9 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UJumpExpression.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +/** + * Represents jump expression (break / continue) with label + */ +interface UJumpExpression : UExpression { + /** + * Returns the expression label, or null if the label is not specified. + */ + val label: String? +} diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/ULabeledExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/ULabeledExpression.kt new file mode 100644 index 000000000000..48a0becd0ed7 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/ULabeledExpression.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents an expression with the label specified. + */ +interface ULabeledExpression : UExpression, ULabeled { + /** + * Returns the expression label. + */ + override val label: String + + /** + * Returns the expression itself. + */ + val expression: UExpression + + override fun accept(visitor: UastVisitor) { + if (visitor.visitLabeledExpression(this)) return + annotations.acceptList(visitor) + expression.accept(visitor) + visitor.afterVisitLabeledExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitLabeledExpression(this, data) + + override fun evaluate() = expression.evaluate() + + override fun asLogString() = log("label = $label") + + override fun asRenderString() = "$label@ ${expression.asRenderString()}" +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/ULambdaExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/ULambdaExpression.kt new file mode 100644 index 000000000000..c9e32defccd5 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/ULambdaExpression.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents the lambda expression. + */ +interface ULambdaExpression : UExpression { + /** + * Returns the list of lambda value parameters. + */ + val valueParameters: List + + /** + * Returns the lambda body expression. + */ + val body: UExpression + + override fun accept(visitor: UastVisitor) { + if (visitor.visitLambdaExpression(this)) return + annotations.acceptList(visitor) + valueParameters.acceptList(visitor) + body.accept(visitor) + visitor.afterVisitLambdaExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitLambdaExpression(this, data) + + override fun asLogString() = log() + + override fun asRenderString(): String { + val renderedValueParameters = if (valueParameters.isEmpty()) + "" + else + valueParameters.joinToString { it.asRenderString() } + " ->" + LINE_SEPARATOR + + return "{ " + renderedValueParameters + body.asRenderString().withMargin + LINE_SEPARATOR + "}" + } +} diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/ULiteralExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/ULiteralExpression.kt new file mode 100644 index 000000000000..6d6027df7dc9 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/ULiteralExpression.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a literal expression. + */ +interface ULiteralExpression : UExpression { + /** + * Returns the literal expression value. + * This is basically a String, Number or null if the literal is a `null` literal. + */ + val value: Any? + + /** + * Returns true if the literal is a `null`-literal, false otherwise. + */ + val isNull: Boolean + get() = value == null + + /** + * Returns true if the literal is a [String] literal, false otherwise. + */ + val isString: Boolean + get() = evaluate() is String + + /** + * Returns true if the literal is a [Boolean] literal, false otherwise. + */ + val isBoolean: Boolean + get() = evaluate() is Boolean + + override fun accept(visitor: UastVisitor) { + if (visitor.visitLiteralExpression(this)) return + annotations.acceptList(visitor) + visitor.afterVisitLiteralExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitLiteralExpression(this, data) + + override fun asRenderString(): String { + val value = value + return when (value) { + null -> "null" + is Char -> "'$value'" + is String -> '"' + value.replace("\\", "\\\\") + .replace("\r", "\\r").replace("\n", "\\n") + .replace("\t", "\\t").replace("\b", "\\b") + .replace("\"", "\\\"") + '"' + else -> value.toString() + } + } + + override fun asLogString() = log("value = ${asRenderString()}") +} diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UNamedExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UNamedExpression.kt new file mode 100644 index 000000000000..9580c1941aa7 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UNamedExpression.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastVisitor + +interface UNamedExpression: UExpression { + val name: String? + val expression: UExpression + + override fun accept(visitor: UastVisitor) { + if (visitor.visitElement(this)) return + annotations.acceptList(visitor) + expression.accept(visitor) + visitor.afterVisitElement(this) + } + + override fun asLogString() = log("name = $name") + + override fun asRenderString() = name + " = " + expression.asRenderString() + + override fun evaluate() = expression.evaluate() +} diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UObjectLiteralExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UObjectLiteralExpression.kt new file mode 100644 index 000000000000..4456f85dbfc3 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UObjectLiteralExpression.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.PsiType +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents an object literal expression, e.g. `new Runnable() {}` in Java. + */ +interface UObjectLiteralExpression : UCallExpression { + /** + * Returns the class declaration. + */ + val declaration: UClass + + override val methodIdentifier: UIdentifier? + get() = null + + override val kind: UastCallKind + get() = UastCallKind.CONSTRUCTOR_CALL + + override val methodName: String? + get() = null + + override val receiver: UExpression? + get() = null + + override val receiverType: PsiType? + get() = null + + override val returnType: PsiType? + get() = null + + + override fun accept(visitor: UastVisitor) { + if (visitor.visitObjectLiteralExpression(this)) return + annotations.acceptList(visitor) + declaration.accept(visitor) + visitor.afterVisitObjectLiteralExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitObjectLiteralExpression(this, data) + + override fun asLogString() = log() + + override fun asRenderString() = "anonymous " + declaration.text +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UParenthesizedExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UParenthesizedExpression.kt new file mode 100644 index 000000000000..64d4963e61f2 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UParenthesizedExpression.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a parenthesized expression, e.g. `(23 + 3)`. + */ +interface UParenthesizedExpression : UExpression { + /** + * Returns an expression inside the parenthesis. + */ + val expression: UExpression + + override fun accept(visitor: UastVisitor) { + if (visitor.visitParenthesizedExpression(this)) return + annotations.acceptList(visitor) + expression.accept(visitor) + visitor.afterVisitParenthesizedExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitParenthesizedExpression(this, data) + + override fun evaluate() = expression.evaluate() + + override fun asLogString() = log() + + override fun asRenderString() = '(' + expression.asRenderString() + ')' +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UPolyadicExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UPolyadicExpression.kt new file mode 100644 index 000000000000..5d69cb5e72f2 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UPolyadicExpression.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a polyadic expression (value1 op value2 op value3 op ...), eg. `2 + "A" + c + d`. + */ +interface UPolyadicExpression : UExpression { + + /** + * Returns a list of expression operands. + */ + val operands: List + + /** + * Returns the operator. + */ + val operator: UastBinaryOperator + + override fun accept(visitor: UastVisitor) { + if (visitor.visitPolyadicExpression(this)) return + annotations.acceptList(visitor) + operands.acceptList(visitor) + visitor.afterVisitPolyadicExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitPolyadicExpression(this, data) + + override fun asLogString() = log("operator = $operator") + + override fun asRenderString() = + operands.joinToString(separator = " ${operator.text} ", transform = UExpression::asRenderString) +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UQualifiedReferenceExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UQualifiedReferenceExpression.kt new file mode 100644 index 000000000000..78f1abc805ef --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UQualifiedReferenceExpression.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents the qualified expression (receiver.selector). + */ +interface UQualifiedReferenceExpression : UReferenceExpression { + /** + * Returns the expression receiver. + */ + val receiver: UExpression + + /** + * Returns the expression selector. + */ + val selector: UExpression + + /** + * Returns the access type (simple, safe access, etc.). + */ + val accessType: UastQualifiedExpressionAccessType + + override fun asRenderString() = receiver.asRenderString() + accessType.name + selector.asRenderString() + + override fun accept(visitor: UastVisitor) { + if (visitor.visitQualifiedReferenceExpression(this)) return + annotations.acceptList(visitor) + receiver.accept(visitor) + selector.accept(visitor) + visitor.afterVisitQualifiedReferenceExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitQualifiedReferenceExpression(this, data) + + override fun asLogString() = log() +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UReferenceExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UReferenceExpression.kt new file mode 100644 index 000000000000..1b26e639a1b4 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UReferenceExpression.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast + +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor + +interface UReferenceExpression : UExpression, UResolvable { + /** + * Returns the resolved name for this reference, or null if the reference can't be resolved. + */ + val resolvedName: String? + + override fun asLogString() = log() + + override fun accept(visitor: UastTypedVisitor, data: D) = visitor.visitReferenceExpression(this, data) +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UReturnExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UReturnExpression.kt new file mode 100644 index 000000000000..0b416a442795 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UReturnExpression.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a `return` expression. + */ +interface UReturnExpression : UExpression { + /** + * Returns the `return` value. + */ + val returnExpression: UExpression? + + override fun accept(visitor: UastVisitor) { + if (visitor.visitReturnExpression(this)) return + annotations.acceptList(visitor) + returnExpression?.accept(visitor) + visitor.afterVisitReturnExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitReturnExpression(this, data) + + override fun asRenderString() = returnExpression.let { if (it == null) "return" else "return " + it.asRenderString() } + + override fun asLogString() = log() +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/USimpleNameReferenceExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/USimpleNameReferenceExpression.kt new file mode 100644 index 000000000000..86aa7de21808 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/USimpleNameReferenceExpression.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a simple reference expression (a non-qualified identifier). + */ +interface USimpleNameReferenceExpression : UReferenceExpression { + /** + * Returns the identifier name. + */ + val identifier: String + + override fun accept(visitor: UastVisitor) { + if (visitor.visitSimpleNameReferenceExpression(this)) return + annotations.acceptList(visitor) + visitor.afterVisitSimpleNameReferenceExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitSimpleNameReferenceExpression(this, data) + + override fun asLogString() = log("identifier = $identifier") + + override fun asRenderString() = identifier +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/USuperExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/USuperExpression.kt new file mode 100644 index 000000000000..cbcc6e76de47 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/USuperExpression.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a `super` expression. + * Qualified `super` is not supported at the moment. + */ +interface USuperExpression : UInstanceExpression { + override fun asLogString() = log("label = $label") + + override fun asRenderString() = "super" + + override fun accept(visitor: UastVisitor) { + if (visitor.visitSuperExpression(this)) return + annotations.acceptList(visitor) + visitor.afterVisitSuperExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitSuperExpression(this, data) +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UThisExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UThisExpression.kt new file mode 100644 index 000000000000..aae64c01b5d9 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UThisExpression.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a `this` expression. + * Qualified `this` is not supported at the moment. + */ +interface UThisExpression : UInstanceExpression { + override fun asLogString() = log("label = $label") + + override fun asRenderString() = "this" + + override fun accept(visitor: UastVisitor) { + if (visitor.visitThisExpression(this)) return + annotations.acceptList(visitor) + visitor.afterVisitThisExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitThisExpression(this, data) +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UThrowExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UThrowExpression.kt new file mode 100644 index 000000000000..6f5d19adc515 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UThrowExpression.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast + +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a `throw` expression. + */ +interface UThrowExpression : UExpression { + /** + * Returns ths thrown expression. + */ + val thrownExpression: UExpression + + override fun accept(visitor: UastVisitor) { + if (visitor.visitThrowExpression(this)) return + annotations.acceptList(visitor) + thrownExpression.accept(visitor) + visitor.afterVisitThrowExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitThrowExpression(this, data) + + override fun asRenderString() = "throw " + thrownExpression.asRenderString() + + override fun asLogString() = log() +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UTypeReferenceExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UTypeReferenceExpression.kt new file mode 100644 index 000000000000..6a8fc9439740 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UTypeReferenceExpression.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.PsiType +import com.intellij.psi.util.PsiTypesUtil +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +interface UTypeReferenceExpression : UExpression { + /** + * Returns the resolved type for this reference. + */ + val type: PsiType + + /** + * Returns the qualified name of the class type, or null if the [type] is not a class type. + */ + fun getQualifiedName() = PsiTypesUtil.getPsiClass(type)?.qualifiedName + + override fun accept(visitor: UastVisitor) { + if (visitor.visitTypeReferenceExpression(this)) return + annotations.acceptList(visitor) + visitor.afterVisitTypeReferenceExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitTypeReferenceExpression(this, data) + + override fun asLogString() = log("name = ${type.name}") + + override fun asRenderString(): String = type.name +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UUnaryExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UUnaryExpression.kt new file mode 100644 index 000000000000..83ca5c947495 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UUnaryExpression.kt @@ -0,0 +1,92 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.PsiMethod +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.internal.log +import org.jetbrains.uast.visitor.UastTypedVisitor +import org.jetbrains.uast.visitor.UastVisitor + +interface UUnaryExpression : UExpression { + /** + * Returns the expression operand. + */ + val operand: UExpression + + /** + * Returns the expression operator. + */ + val operator: UastOperator + + /** + * Returns the operator identifier. + */ + val operatorIdentifier: UIdentifier? + + /** + * Resolve the operator method. + * + * @return the resolved method, or null if the method can't be resolved, or if the expression is not a method call. + */ + fun resolveOperator(): PsiMethod? + + override fun accept(visitor: UastVisitor) { + if (visitor.visitUnaryExpression(this)) return + annotations.acceptList(visitor) + operand.accept(visitor) + visitor.afterVisitUnaryExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitUnaryExpression(this, data) +} + +interface UPrefixExpression : UUnaryExpression { + override val operator: UastPrefixOperator + + override fun accept(visitor: UastVisitor) { + if (visitor.visitPrefixExpression(this)) return + annotations.acceptList(visitor) + operand.accept(visitor) + visitor.afterVisitPrefixExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitPrefixExpression(this, data) + + override fun asLogString() = log("operator = $operator") + + override fun asRenderString() = operator.text + operand.asRenderString() +} + +interface UPostfixExpression : UUnaryExpression { + override val operator: UastPostfixOperator + + override fun accept(visitor: UastVisitor) { + if (visitor.visitPostfixExpression(this)) return + annotations.acceptList(visitor) + operand.accept(visitor) + visitor.afterVisitPostfixExpression(this) + } + + override fun accept(visitor: UastTypedVisitor, data: D) = + visitor.visitPostfixExpression(this, data) + + override fun asLogString() = log("operator = $operator") + + override fun asRenderString() = operand.asRenderString() + operator.text +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/uastLiteralUtils.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/uastLiteralUtils.kt new file mode 100644 index 000000000000..e8dec4910a75 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/uastLiteralUtils.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:JvmName("UastLiteralUtils") +package org.jetbrains.uast + +/** + * Checks if the [UElement] is a null literal. + * + * @return true if the receiver is a null literal, false otherwise. + */ +fun UElement.isNullLiteral(): Boolean = this is ULiteralExpression && this.isNull + +/** + * Checks if the [UElement] is a boolean literal. + * + * @return true if the receiver is a boolean literal, false otherwise. + */ +fun UElement.isBooleanLiteral(): Boolean = this is ULiteralExpression && this.isBoolean + +/** + * Checks if the [UElement] is a `true` boolean literal. + * + * @return true if the receiver is a `true` boolean literal, false otherwise. + */ +fun UElement.isTrueLiteral(): Boolean = this is ULiteralExpression && this.isBoolean && this.value == true + +/** + * Checks if the [UElement] is a `false` boolean literal. + * + * @return true if the receiver is a `false` boolean literal, false otherwise. + */ +fun UElement.isFalseLiteral(): Boolean = this is ULiteralExpression && this.isBoolean && this.value == false + +/** + * Checks if the [UElement] is a [String] literal. + * + * @return true if the receiver is a [String] literal, false otherwise. + */ +fun UElement.isStringLiteral(): Boolean = this is ULiteralExpression && this.isString + +/** + * Returns the [String] literal value. + * + * @return literal text if the receiver is a valid [String] literal, null otherwise. + */ +fun UElement.getValueIfStringLiteral(): String? = + if (isStringLiteral()) (this as ULiteralExpression).value as String else null + +/** + * Checks if the [UElement] is a [Number] literal (Integer, Long, Float, Double, etc.). + * + * @return true if the receiver is a [Number] literal, false otherwise. + */ +fun UElement.isNumberLiteral(): Boolean = this is ULiteralExpression && this.value is Number + +/** + * Checks if the [UElement] is an integral literal (is an [Integer], [Long], [Short], [Char] or [Byte]). + * + * @return true if the receiver is an integral literal, false otherwise. + */ +fun UElement.isIntegralLiteral(): Boolean = this is ULiteralExpression && when (value) { + is Int -> true + is Long -> true + is Short -> true + is Char -> true + is Byte -> true + else -> false +} + +/** + * Returns the integral value of the literal. + * + * @return long representation of the literal expression value, + * 0 if the receiver literal expression is not a integral one. + */ +fun ULiteralExpression.getLongValue(): Long = value.let { + when (it) { + is Long -> it + is Int -> it.toLong() + is Short -> it.toLong() + is Char -> it.toLong() + is Byte -> it.toLong() + else -> 0 + } +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/internal/implementationUtils.kt b/uast/uast-common/src/org/jetbrains/uast/internal/implementationUtils.kt new file mode 100644 index 000000000000..5c5301a15cbd --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/internal/implementationUtils.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.internal + +import org.jetbrains.uast.UElement +import org.jetbrains.uast.visitor.UastVisitor + +fun List.acceptList(visitor: UastVisitor) { + for (element in this) { + element.accept(visitor) + } +} + +@Suppress("unused") +inline fun T.log(text: String = ""): String { + val className = T::class.java.simpleName + return if (text.isEmpty()) className else "$className ($text)" +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/internal/internalUastUtils.kt b/uast/uast-common/src/org/jetbrains/uast/internal/internalUastUtils.kt new file mode 100644 index 000000000000..24cf3cfd514b --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/internal/internalUastUtils.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.PsiModifier +import com.intellij.psi.PsiModifierListOwner +import com.intellij.psi.PsiType + +internal val LINE_SEPARATOR = System.getProperty("line.separator") ?: "\n" + +val String.withMargin: String + get() = lines().joinToString(LINE_SEPARATOR) { " " + it } + +internal operator fun String.times(n: Int) = this.repeat(n) + +internal fun List.asLogString() = joinToString(LINE_SEPARATOR) { it.asLogString().withMargin } + +internal tailrec fun UExpression.unwrapParenthesis(): UExpression = when (this) { + is UParenthesizedExpression -> expression.unwrapParenthesis() + else -> this +} + +internal fun lz(f: () -> T) = lazy(LazyThreadSafetyMode.NONE, f) + +internal val PsiType.name: String + get() = getCanonicalText(false) + + +internal fun PsiModifierListOwner.renderModifiers(): String { + val modifiers = PsiModifier.MODIFIERS.filter { hasModifierProperty(it) }.joinToString(" ") + return if (modifiers.isEmpty()) "" else modifiers + " " +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/kinds/UastBinaryExpressionWithTypeKind.kt b/uast/uast-common/src/org/jetbrains/uast/kinds/UastBinaryExpressionWithTypeKind.kt new file mode 100644 index 000000000000..1e386ff0e3db --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/kinds/UastBinaryExpressionWithTypeKind.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:JvmName("UastBinaryExpressionWithTypeUtils") +package org.jetbrains.uast + +/** + * Kinds of [UBinaryExpressionWithType]. + * Examples: type casts, instance checks. + */ +open class UastBinaryExpressionWithTypeKind(val name: String) { + open class TypeCast(name: String) : UastBinaryExpressionWithTypeKind(name) + open class InstanceCheck(name: String) : UastBinaryExpressionWithTypeKind(name) + + companion object { + @JvmField + val TYPE_CAST = TypeCast("as") + + @JvmField + val INSTANCE_CHECK = InstanceCheck("is") + + @JvmField + val UNKNOWN = UastBinaryExpressionWithTypeKind("") + } +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/kinds/UastBinaryOperator.kt b/uast/uast-common/src/org/jetbrains/uast/kinds/UastBinaryOperator.kt new file mode 100644 index 000000000000..e68be7e32575 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/kinds/UastBinaryOperator.kt @@ -0,0 +1,133 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +/** + * Kinds of operators in [UBinaryExpression]. + */ +open class UastBinaryOperator(override val text: String): UastOperator { + class LogicalOperator(text: String): UastBinaryOperator(text) + class ComparisonOperator(text: String): UastBinaryOperator(text) + class ArithmeticOperator(text: String): UastBinaryOperator(text) + class BitwiseOperator(text: String): UastBinaryOperator(text) + class AssignOperator(text: String): UastBinaryOperator(text) + + companion object { + @JvmField + val ASSIGN = AssignOperator("=") + + @JvmField + val PLUS = ArithmeticOperator("+") + + @JvmField + val MINUS = ArithmeticOperator("-") + + @JvmField + val MULTIPLY = ArithmeticOperator("*") + + @JvmField + val DIV = ArithmeticOperator("/") + + @JvmField + val MOD = ArithmeticOperator("%") + + @JvmField + val LOGICAL_OR = LogicalOperator("||") + + @JvmField + val LOGICAL_AND = LogicalOperator("&&") + + @JvmField + val BITWISE_OR = BitwiseOperator("|") + + @JvmField + val BITWISE_AND = BitwiseOperator("&") + + @JvmField + val BITWISE_XOR = BitwiseOperator("^") + + @JvmField + val EQUALS = ComparisonOperator("==") + + @JvmField + val NOT_EQUALS = ComparisonOperator("!=") + + @JvmField + val IDENTITY_EQUALS = ComparisonOperator("===") + + @JvmField + val IDENTITY_NOT_EQUALS = ComparisonOperator("!==") + + @JvmField + val GREATER = ComparisonOperator(">") + + @JvmField + val GREATER_OR_EQUALS = ComparisonOperator(">=") + + @JvmField + val LESS = ComparisonOperator("<") + + @JvmField + val LESS_OR_EQUALS = ComparisonOperator("<=") + + @JvmField + val SHIFT_LEFT = BitwiseOperator("<<") + + @JvmField + val SHIFT_RIGHT = BitwiseOperator(">>") + + @JvmField + val UNSIGNED_SHIFT_RIGHT = BitwiseOperator(">>>") + + @JvmField + val OTHER = UastBinaryOperator("") + + @JvmField + val PLUS_ASSIGN = AssignOperator("+=") + + @JvmField + val MINUS_ASSIGN = AssignOperator("-=") + + @JvmField + val MULTIPLY_ASSIGN = AssignOperator("*=") + + @JvmField + val DIVIDE_ASSIGN = AssignOperator("/=") + + @JvmField + val REMAINDER_ASSIGN = AssignOperator("%=") + + @JvmField + val AND_ASSIGN = AssignOperator("&=") + + @JvmField + val XOR_ASSIGN = AssignOperator("^=") + + @JvmField + val OR_ASSIGN = AssignOperator("|=") + + @JvmField + val SHIFT_LEFT_ASSIGN = AssignOperator("<<=") + + @JvmField + val SHIFT_RIGHT_ASSIGN = AssignOperator(">>=") + + @JvmField + val UNSIGNED_SHIFT_RIGHT_ASSIGN = AssignOperator(">>>=") + } + + override fun toString() = text +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/kinds/UastCallKind.kt b/uast/uast-common/src/org/jetbrains/uast/kinds/UastCallKind.kt new file mode 100644 index 000000000000..9d069dc3a447 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/kinds/UastCallKind.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +/** + * Kinds of [UCallExpression]. + */ +open class UastCallKind(val name: String) { + companion object { + @JvmField + val METHOD_CALL = UastCallKind("method_call") + + @JvmField + val CONSTRUCTOR_CALL = UastCallKind("constructor_call") + + @JvmField + val NEW_ARRAY_WITH_DIMENSIONS = UastCallKind("new_array_with_dimensions") + + /** + * Initializer parts are available in call expression as value arguments. + * [NEW_ARRAY_WITH_INITIALIZER] is a top-level initializer. In case of multi-dimensional arrays, inner initializers + * have type of [NESTED_ARRAY_INITIALIZER]. + */ + @JvmField + val NEW_ARRAY_WITH_INITIALIZER = UastCallKind("new_array_with_initializer") + + @JvmField + val NESTED_ARRAY_INITIALIZER = UastCallKind("array_initializer") + } + + override fun toString(): String{ + return "UastCallKind(name='$name')" + } +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/kinds/UastClassKind.kt b/uast/uast-common/src/org/jetbrains/uast/kinds/UastClassKind.kt new file mode 100644 index 000000000000..563c8fb0a2fe --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/kinds/UastClassKind.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast + +/** + * Kinds of [UClass]. + */ +open class UastClassKind(val text: String) { + companion object { + @JvmField + val CLASS = UastClassKind("class") + + @JvmField + val INTERFACE = UastClassKind("interface") + + @JvmField + val ANNOTATION = UastClassKind("annotation") + + @JvmField + val ENUM = UastClassKind("enum") + + @JvmField + val OBJECT = UastClassKind("object") + } + + override fun toString(): String{ + return "UastClassKind(text='$text')" + } +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/kinds/UastOperator.kt b/uast/uast-common/src/org/jetbrains/uast/kinds/UastOperator.kt new file mode 100644 index 000000000000..790be93bc134 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/kinds/UastOperator.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +/** + * Uast operator base interface. + * + * @see [UastPrefixOperator], [UastPostfixOperator], [UastBinaryOperator] + */ +interface UastOperator { + /** + * Returns the operator text to render in [UElement.asRenderString]. + */ + val text: String +} \ No newline at end of file diff --git a/build/scripts/tests.gant b/uast/uast-common/src/org/jetbrains/uast/kinds/UastPostfixOperator.kt similarity index 53% rename from build/scripts/tests.gant rename to uast/uast-common/src/org/jetbrains/uast/kinds/UastPostfixOperator.kt index 4a943ded52f7..db9342a56e6e 100644 --- a/build/scripts/tests.gant +++ b/uast/uast-common/src/org/jetbrains/uast/kinds/UastPostfixOperator.kt @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,27 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import static org.jetbrains.jps.idea.IdeaProjectLoader.guessHome +package org.jetbrains.uast -def home = guessHome(this) +/** + * [UPostfixExpression] operators. + */ +open class UastPostfixOperator(override val text: String): UastOperator { + companion object { + @JvmField + val INC = UastPostfixOperator("++") -includeTargets << new File("${home}/build/scripts/common_tests.gant") + @JvmField + val DEC = UastPostfixOperator("--") -def args = [] + @JvmField + val UNKNOWN = UastPostfixOperator("") + } -def mem = System.getProperty("test.jvm.memory") -if (mem != null) { - args.addAll(mem.split()) + override fun toString() = text } -else { - args += [ - "-Xmx450m", - "-Dsun.io.useCanonCaches=false" - ] -} - -args << "-Djna.nosys=true" - -setProperty("jvm_args", args) - -setProperty("testcases", ["com.intellij.AllTests"]) diff --git a/uast/uast-common/src/org/jetbrains/uast/kinds/UastPrefixOperator.kt b/uast/uast-common/src/org/jetbrains/uast/kinds/UastPrefixOperator.kt new file mode 100644 index 000000000000..a6ea6e94c0bc --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/kinds/UastPrefixOperator.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +/** + * [UPrefixExpression] operators. + */ +class UastPrefixOperator(override val text: String): UastOperator { + companion object { + @JvmField + val INC = UastPrefixOperator("++") + + @JvmField + val DEC = UastPrefixOperator("--") + + @JvmField + val UNARY_MINUS = UastPrefixOperator("-") + + @JvmField + val UNARY_PLUS = UastPrefixOperator("+") + + @JvmField + val LOGICAL_NOT = UastPrefixOperator("!") + + @JvmField + val BITWISE_NOT = UastPrefixOperator("~") + + @JvmField + val UNKNOWN = UastPrefixOperator("") + } + + override fun toString() = text +} diff --git a/uast/uast-common/src/org/jetbrains/uast/kinds/UastQualifiedExpressionAccessType.kt b/uast/uast-common/src/org/jetbrains/uast/kinds/UastQualifiedExpressionAccessType.kt new file mode 100644 index 000000000000..1b7115423810 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/kinds/UastQualifiedExpressionAccessType.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +/** + * Access types of [UQualifiedReferenceExpression]. + * Additional type examples: Kotlin safe call (?.). + */ +open class UastQualifiedExpressionAccessType(val name: String) { + companion object { + @JvmField + val SIMPLE = UastQualifiedExpressionAccessType(".") + } + + override fun toString(): String{ + return "UastQualifiedExpressionAccessType(name='$name')" + } +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/kinds/UastSpecialExpressionKind.kt b/uast/uast-common/src/org/jetbrains/uast/kinds/UastSpecialExpressionKind.kt new file mode 100644 index 000000000000..83c041a7b69f --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/kinds/UastSpecialExpressionKind.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +/** + * Kinds of [UExpressionList]. + */ +open class UastSpecialExpressionKind(val name: String) { + override fun toString(): String{ + return "UastSpecialExpressionKind(name='$name')" + } +} diff --git a/uast/uast-common/src/org/jetbrains/uast/kinds/UastVisibility.kt b/uast/uast-common/src/org/jetbrains/uast/kinds/UastVisibility.kt new file mode 100644 index 000000000000..124071a87ccd --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/kinds/UastVisibility.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast + +import com.intellij.psi.PsiLocalVariable +import com.intellij.psi.PsiModifier +import com.intellij.psi.PsiModifierListOwner + +enum class UastVisibility(val text: String) { + PUBLIC("public"), + PRIVATE("private"), + PROTECTED("protected"), + PACKAGE_LOCAL("packageLocal"), + LOCAL("local"); + + override fun toString() = text + + companion object { + operator fun get(declaration: PsiModifierListOwner): UastVisibility { + if (declaration.hasModifierProperty(PsiModifier.PUBLIC)) return UastVisibility.PUBLIC + if (declaration.hasModifierProperty(PsiModifier.PROTECTED)) return UastVisibility.PROTECTED + if (declaration.hasModifierProperty(PsiModifier.PRIVATE)) return UastVisibility.PRIVATE + if (declaration is PsiLocalVariable) return UastVisibility.LOCAL + return UastVisibility.PACKAGE_LOCAL + } + } +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/psi/UElementWithLocation.kt b/uast/uast-common/src/org/jetbrains/uast/psi/UElementWithLocation.kt new file mode 100644 index 000000000000..fa47f99f862e --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/psi/UElementWithLocation.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.psi + +import com.intellij.openapi.util.Segment +import org.jetbrains.uast.UElement + +interface UElementWithLocation : UElement, Segment \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/psi/UastPsiParameterNotResolved.kt b/uast/uast-common/src/org/jetbrains/uast/psi/UastPsiParameterNotResolved.kt new file mode 100644 index 000000000000..b1452f80b288 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/psi/UastPsiParameterNotResolved.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.psi + +import com.intellij.lang.Language +import com.intellij.psi.PsiElement +import com.intellij.psi.impl.light.LightParameter +import org.jetbrains.uast.UastErrorType + +class UastPsiParameterNotResolved( + declarationScope: PsiElement, + language: Language +) : LightParameter("error", UastErrorType, declarationScope, language) \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/qualifiedUtils.kt b/uast/uast-common/src/org/jetbrains/uast/qualifiedUtils.kt new file mode 100644 index 000000000000..f63c35b68d0a --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/qualifiedUtils.kt @@ -0,0 +1,199 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:JvmMultifileClass +@file:JvmName("UastUtils") +package org.jetbrains.uast + +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Get the topmost parent qualified expression for the call expression. + * + * Example 1: + * Code: variable.call(args) + * Call element: E = call(args) + * Qualified parent (return value): Q = [getQualifiedCallElement](E) = variable.call(args) + * + * Example 2: + * Code: call(args) + * Call element: E = call(args) + * Qualified parent (return value): Q = [getQualifiedCallElement](E) = call(args) (no qualifier) + * + * @return containing qualified expression if the call is a child of the qualified expression, call element otherwise. + */ +fun UExpression.getQualifiedParentOrThis(): UExpression { + fun findParent(current: UExpression?, previous: UExpression): UExpression? = when (current) { + is UQualifiedReferenceExpression -> { + if (current.selector == previous) + findParent(current.uastParent as? UExpression, current) ?: current + else + previous + } + is UParenthesizedExpression -> findParent(current.expression, previous) ?: previous + else -> null + } + + return findParent(uastParent as? UExpression, this) ?: this +} + + +fun UExpression.asQualifiedPath(): List? { + if (this is USimpleNameReferenceExpression) { + return listOf(this.identifier) + } else if (this !is UQualifiedReferenceExpression) { + return null + } + + var error = false + val list = mutableListOf() + fun addIdentifiers(expr: UQualifiedReferenceExpression) { + val receiver = expr.receiver.unwrapParenthesis() + val selector = expr.selector as? USimpleNameReferenceExpression ?: run { error = true; return } + when (receiver) { + is UQualifiedReferenceExpression -> addIdentifiers(receiver) + is USimpleNameReferenceExpression -> list += receiver.identifier + else -> { + error = true + return + } + } + list += selector.identifier + } + + addIdentifiers(this) + return if (error) null else list +} + +/** + * Return the list of qualified expressions. + * + * Example: + * Code: obj.call(param).anotherCall(param2).getter + * Qualified chain: [obj, call(param), anotherCall(param2), getter] + * + * @return list of qualified expressions, or the empty list if the received expression is not a qualified expression. + */ +fun UExpression?.getQualifiedChain(): List { + fun collect(expr: UQualifiedReferenceExpression, chains: MutableList) { + val receiver = expr.receiver.unwrapParenthesis() + if (receiver is UQualifiedReferenceExpression) { + collect(receiver, chains) + } else { + chains += receiver + } + + val selector = expr.selector.unwrapParenthesis() + if (selector is UQualifiedReferenceExpression) { + collect(selector, chains) + } else { + chains += selector + } + } + + if (this == null) return emptyList() + val qualifiedExpression = this as? UQualifiedReferenceExpression ?: return listOf(this) + val chains = mutableListOf() + collect(qualifiedExpression, chains) + return chains +} + +/** + * Return the outermost qualified expression. + * + * @return the outermost qualified expression, + * this element if the parent expression is not a qualified expression, + * or null if the element is not a qualified expression. + * + * Example: + * Code: a.b.c(asd).g + * Call element: c(asd) + * Outermost qualified (return value): a.b.c(asd).g + */ +fun UExpression.getOutermostQualified(): UQualifiedReferenceExpression? { + tailrec fun getOutermostQualified(current: UElement?, previous: UExpression): UQualifiedReferenceExpression? = when (current) { + is UQualifiedReferenceExpression -> getOutermostQualified(current.uastParent, current) + is UParenthesizedExpression -> getOutermostQualified(current.uastParent, previous) + else -> if (previous is UQualifiedReferenceExpression) previous else null + } + + return getOutermostQualified(this.uastParent, this) +} + +/** + * Checks if the received expression is a qualified chain of identifiers, and the trailing part of such chain is [fqName]. + * + * @param fqName the chain part to check against. Sequence of identifiers, separated by dot ('.'). Example: "com.example". + * @return true, if the received expression is a qualified chain of identifiers, and the trailing part of such chain is [fqName]. + */ +fun UExpression.matchesQualified(fqName: String): Boolean { + val identifiers = this.asQualifiedPath() ?: return false + val passedIdentifiers = fqName.trim('.').split('.') + return identifiers == passedIdentifiers +} + +/** + * Checks if the received expression is a qualified chain of identifiers, and the leading part of such chain is [fqName]. + * + * @param fqName the chain part to check against. Sequence of identifiers, separated by dot ('.'). Example: "com.example". + * @return true, if the received expression is a qualified chain of identifiers, and the leading part of such chain is [fqName]. + */ +fun UExpression.startsWithQualified(fqName: String): Boolean { + val identifiers = this.asQualifiedPath() ?: return false + val passedIdentifiers = fqName.trim('.').split('.') + if (identifiers.size < passedIdentifiers.size) return false + passedIdentifiers.forEachIndexed { i, passedIdentifier -> + if (passedIdentifier != identifiers[i]) return false + } + return true +} + +/** + * Checks if the received expression is a qualified chain of identifiers, and the trailing part of such chain is [fqName]. + * + * @param fqName the chain part to check against. Sequence of identifiers, separated by dot ('.'). Example: "com.example". + * @return true, if the received expression is a qualified chain of identifiers, and the trailing part of such chain is [fqName]. + */ +fun UExpression.endsWithQualified(fqName: String): Boolean { + val identifiers = this.asQualifiedPath()?.asReversed() ?: return false + val passedIdentifiers = fqName.trim('.').split('.').asReversed() + if (identifiers.size < passedIdentifiers.size) return false + passedIdentifiers.forEachIndexed { i, passedIdentifier -> + if (passedIdentifier != identifiers[i]) return false + } + return true +} + +fun UElement.asRecursiveLogString(): String { + val stringBuilder = StringBuilder() + val indent = " " + + accept(object : UastVisitor { + private var level = 0 + + override fun visitElement(node: UElement): Boolean { + stringBuilder.append(indent.repeat(level)) + stringBuilder.appendln(node.asLogString()) + level++ + return false + } + + override fun afterVisitElement(node: UElement) { + super.afterVisitElement(node) + level-- + } + }) + return stringBuilder.toString() +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/util/callUtils.kt b/uast/uast-common/src/org/jetbrains/uast/util/callUtils.kt new file mode 100644 index 000000000000..dc9eb32792af --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/util/callUtils.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:JvmName("UastExpressionUtils") +package org.jetbrains.uast.util + +import org.jetbrains.uast.* + +fun UElement.isConstructorCall() = (this as? UCallExpression)?.kind == UastCallKind.CONSTRUCTOR_CALL + +fun UElement.isMethodCall() = (this as? UCallExpression)?.kind == UastCallKind.METHOD_CALL + +fun UElement.isNewArray() = isNewArrayWithDimensions() || isNewArrayWithInitializer() + +fun UElement.isNewArrayWithDimensions() = (this as? UCallExpression)?.kind == UastCallKind.NEW_ARRAY_WITH_DIMENSIONS + +fun UElement.isNewArrayWithInitializer() = (this as? UCallExpression)?.kind == UastCallKind.NEW_ARRAY_WITH_INITIALIZER + +@Deprecated("Use isArrayInitializer()", ReplaceWith("isArrayInitializer()")) +fun UElement.isNestedArrayInitializer() = isArrayInitializer() + +fun UElement.isArrayInitializer() = (this as? UCallExpression)?.kind == UastCallKind.NESTED_ARRAY_INITIALIZER + +fun UElement.isTypeCast() = (this as? UBinaryExpressionWithType)?.operationKind is UastBinaryExpressionWithTypeKind.TypeCast + +fun UElement.isInstanceCheck() = (this as? UBinaryExpressionWithType)?.operationKind is UastBinaryExpressionWithTypeKind.InstanceCheck + +fun UElement.isAssignment() = (this as? UBinaryExpression)?.operator is UastBinaryOperator.AssignOperator + +fun UVariable.isResourceVariable() = uastParent is UTryExpression diff --git a/uast/uast-common/src/org/jetbrains/uast/values/UCallResultValue.kt b/uast/uast-common/src/org/jetbrains/uast/values/UCallResultValue.kt new file mode 100644 index 000000000000..1b56a2fe5bd9 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/values/UCallResultValue.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.values + +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UResolvable + +// Value of something resolvable (e.g. call or property access) +// that we cannot or do not want to evaluate +class UCallResultValue(val resolvable: UResolvable, val arguments: List) : UValueBase(), UDependency { + override fun equals(other: Any?) = other is UCallResultValue && resolvable == other.resolvable && arguments == other.arguments + + override fun hashCode() = resolvable.hashCode() + + override fun toString(): String { + return "external ${(resolvable as? UElement)?.asRenderString() ?: "???"}(${arguments.joinToString()})" + } +} diff --git a/uast/uast-common/src/org/jetbrains/uast/values/UConstant.kt b/uast/uast-common/src/org/jetbrains/uast/values/UConstant.kt new file mode 100644 index 000000000000..9cc7d52b5dd8 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/values/UConstant.kt @@ -0,0 +1,426 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.values + +import com.intellij.psi.PsiEnumConstant +import com.intellij.psi.PsiType +import org.jetbrains.uast.* + +interface UConstant : UValue { + val value: Any? + + val source: UExpression? + + // Used for string concatenation + fun asString(): String + + // Used for logging / debugging purposes + override fun toString(): String +} + +abstract class UAbstractConstant : UValueBase(), UConstant { + override fun valueEquals(other: UValue) = when (other) { + this -> UBooleanConstant.True + is UConstant -> UBooleanConstant.False + else -> super.valueEquals(other) + } + + override fun equals(other: Any?) = other is UAbstractConstant && value == other.value + + override fun hashCode() = value?.hashCode() ?: 0 + + override fun toString() = "$value" + + override fun asString() = toString() +} + +enum class UNumericType(val prefix: String = "") { + BYTE("(byte)"), + SHORT("(short)"), + INT(), + LONG("(long)"), + FLOAT("(float)"), + DOUBLE(); + + fun merge(other: UNumericType): UNumericType { + if (this == DOUBLE || other == DOUBLE) return DOUBLE + if (this == FLOAT || other == FLOAT) return FLOAT + if (this == LONG || other == LONG) return LONG + return INT + } +} + +abstract class UNumericConstant(val type: UNumericType, override val source: ULiteralExpression?) : UAbstractConstant() { + override abstract val value: Number + + override fun toString() = "${type.prefix}$value" + + override fun asString() = "$value" +} + +private fun PsiType.toNumeric(): UNumericType = when (this) { + PsiType.LONG -> UNumericType.LONG + PsiType.INT -> UNumericType.INT + PsiType.SHORT -> UNumericType.SHORT + PsiType.BYTE -> UNumericType.BYTE + PsiType.DOUBLE -> UNumericType.DOUBLE + PsiType.FLOAT -> UNumericType.FLOAT + else -> throw AssertionError("Conversion is impossible for type $canonicalText") +} + +private fun Int.asType(type: UNumericType): Int = when (type) { + UNumericType.BYTE -> toByte().toInt() + UNumericType.SHORT -> toShort().toInt() + else -> this +} + +class UIntConstant( + rawValue: Int, type: UNumericType = UNumericType.INT, override val source: ULiteralExpression? = null +) : UNumericConstant(type, source) { + + init { + when (type) { + UNumericType.INT, UNumericType.SHORT, UNumericType.BYTE -> {} + else -> throw AssertionError("Incorrect UIntConstant type: $type") + } + } + + override val value: Int = rawValue.asType(type) + + constructor(value: Int, type: PsiType): this(value, type.toNumeric()) + + override fun plus(other: UValue) = when (other) { + is UIntConstant -> UIntConstant(value + other.value, type.merge(other.type)) + is ULongConstant -> other + this + is UFloatConstant -> other + this + else -> super.plus(other) + } + + override fun times(other: UValue) = when (other) { + is UIntConstant -> UIntConstant(value * other.value, type.merge(other.type)) + is ULongConstant -> other * this + is UFloatConstant -> other * this + else -> super.times(other) + } + + override fun div(other: UValue) = when (other) { + is UIntConstant -> UIntConstant(value / other.value, type.merge(other.type)) + is ULongConstant -> ULongConstant(value / other.value) + is UFloatConstant -> UFloatConstant.create(value / other.value, type.merge(other.type)) + else -> super.div(other) + } + + override fun mod(other: UValue) = when (other) { + is UIntConstant -> UIntConstant(value % other.value, type.merge(other.type)) + is ULongConstant -> ULongConstant(value % other.value) + is UFloatConstant -> UFloatConstant.create(value % other.value, type.merge(other.type)) + else -> super.mod(other) + } + + override fun unaryMinus() = UIntConstant(-value, type) + + override fun greater(other: UValue) = when (other) { + is UIntConstant -> UBooleanConstant.valueOf(value > other.value) + is ULongConstant -> UBooleanConstant.valueOf(value > other.value) + is UFloatConstant -> UBooleanConstant.valueOf(value > other.value) + else -> super.greater(other) + } + + override fun inc() = UIntConstant(value + 1, type) + + override fun dec() = UIntConstant(value - 1, type) + + override fun bitwiseAnd(other: UValue) = when (other) { + is UIntConstant -> UIntConstant(value and other.value, type.merge(other.type)) + else -> super.bitwiseAnd(other) + } + + override fun bitwiseOr(other: UValue) = when (other) { + is UIntConstant -> UIntConstant(value or other.value, type.merge(other.type)) + else -> super.bitwiseOr(other) + } + + override fun bitwiseXor(other: UValue) = when (other) { + is UIntConstant -> UIntConstant(value xor other.value, type.merge(other.type)) + else -> super.bitwiseXor(other) + } + + override fun shl(other: UValue) = when (other) { + is UIntConstant -> UIntConstant(value shl other.value, type.merge(other.type)) + else -> super.shl(other) + } + + override fun shr(other: UValue) = when (other) { + is UIntConstant -> UIntConstant(value shr other.value, type.merge(other.type)) + else -> super.shr(other) + } + + override fun ushr(other: UValue) = when (other) { + is UIntConstant -> UIntConstant(value ushr other.value, type.merge(other.type)) + else -> super.ushr(other) + } +} + +class ULongConstant(override val value: Long, source: ULiteralExpression? = null) : UNumericConstant(UNumericType.LONG, source) { + override fun plus(other: UValue) = when (other) { + is ULongConstant -> ULongConstant(value + other.value) + is UIntConstant -> ULongConstant(value + other.value) + is UFloatConstant -> other + this + else -> super.plus(other) + } + + override fun times(other: UValue) = when (other) { + is ULongConstant -> ULongConstant(value * other.value) + is UIntConstant -> ULongConstant(value * other.value) + is UFloatConstant -> other * this + else -> super.times(other) + } + + override fun div(other: UValue) = when (other) { + is ULongConstant -> ULongConstant(value / other.value) + is UIntConstant -> ULongConstant(value / other.value) + is UFloatConstant -> UFloatConstant.create(value / other.value, type.merge(other.type)) + else -> super.div(other) + } + + override fun mod(other: UValue) = when (other) { + is ULongConstant -> ULongConstant(value % other.value) + is UIntConstant -> ULongConstant(value % other.value) + is UFloatConstant -> UFloatConstant.create(value % other.value, type.merge(other.type)) + else -> super.mod(other) + } + + override fun unaryMinus() = ULongConstant(-value) + + override fun greater(other: UValue) = when (other) { + is ULongConstant -> UBooleanConstant.valueOf(value > other.value) + is UIntConstant -> UBooleanConstant.valueOf(value > other.value) + is UFloatConstant -> UBooleanConstant.valueOf(value > other.value) + else -> super.greater(other) + } + + override fun inc() = ULongConstant(value + 1) + + override fun dec() = ULongConstant(value - 1) + + override fun bitwiseAnd(other: UValue) = when (other) { + is ULongConstant -> ULongConstant(value and other.value) + else -> super.bitwiseAnd(other) + } + + override fun bitwiseOr(other: UValue) = when (other) { + is ULongConstant -> ULongConstant(value or other.value) + else -> super.bitwiseOr(other) + } + + override fun bitwiseXor(other: UValue) = when (other) { + is ULongConstant -> ULongConstant(value xor other.value) + else -> super.bitwiseXor(other) + } + + override fun shl(other: UValue) = when (other) { + is UIntConstant -> ULongConstant(value shl other.value) + else -> super.shl(other) + } + + override fun shr(other: UValue) = when (other) { + is UIntConstant -> ULongConstant(value shr other.value) + else -> super.shr(other) + } + + override fun ushr(other: UValue) = when (other) { + is UIntConstant -> ULongConstant(value ushr other.value) + else -> super.ushr(other) + } +} + +open class UFloatConstant protected constructor( + override val value: Double, type: UNumericType = UNumericType.DOUBLE, source: ULiteralExpression? = null +) : UNumericConstant(type, source) { + + override fun plus(other: UValue) = when (other) { + is ULongConstant -> create(value + other.value, type.merge(other.type)) + is UIntConstant -> create(value + other.value, type.merge(other.type)) + is UFloatConstant -> create(value + other.value, type.merge(other.type)) + else -> super.plus(other) + } + + override fun times(other: UValue) = when (other) { + is ULongConstant -> create(value * other.value, type.merge(other.type)) + is UIntConstant -> create(value * other.value, type.merge(other.type)) + is UFloatConstant -> create(value * other.value, type.merge(other.type)) + else -> super.times(other) + } + + override fun div(other: UValue) = when (other) { + is ULongConstant -> create(value / other.value, type.merge(other.type)) + is UIntConstant -> create(value / other.value, type.merge(other.type)) + is UFloatConstant -> create(value / other.value, type.merge(other.type)) + else -> super.div(other) + } + + override fun mod(other: UValue) = when (other) { + is ULongConstant -> create(value % other.value, type.merge(other.type)) + is UIntConstant -> create(value % other.value, type.merge(other.type)) + is UFloatConstant -> create(value % other.value, type.merge(other.type)) + else -> super.mod(other) + } + + override fun greater(other: UValue) = when (other) { + is ULongConstant -> UBooleanConstant.valueOf(value > other.value) + is UIntConstant -> UBooleanConstant.valueOf(value > other.value) + is UFloatConstant -> UBooleanConstant.valueOf(value > other.value) + else -> super.greater(other) + } + + override fun unaryMinus() = create(-value, type) + + override fun inc() = create(value + 1, type) + + override fun dec() = create(value - 1, type) + + companion object { + fun create(value: Double, type: UNumericType = UNumericType.DOUBLE, source: ULiteralExpression? = null) = + when (type) { + UNumericType.DOUBLE, UNumericType.FLOAT -> { + if (value.isNaN()) UNaNConstant.valueOf(type) + else UFloatConstant(value, type, source) + } + else -> throw AssertionError("Incorrect UFloatConstant type: $type") + } + + fun create(value: Double, type: PsiType) = create(value, type.toNumeric()) + } +} + +sealed class UNaNConstant(type: UNumericType = UNumericType.DOUBLE) : UFloatConstant(kotlin.Double.NaN, type) { + object Float : UNaNConstant(UNumericType.FLOAT) + + object Double : UNaNConstant(UNumericType.DOUBLE) + + override fun greater(other: UValue) = UBooleanConstant.False + + override fun less(other: UValue) = UBooleanConstant.False + + override fun greaterOrEquals(other: UValue) = UBooleanConstant.False + + override fun lessOrEquals(other: UValue) = UBooleanConstant.False + + override fun valueEquals(other: UValue) = UBooleanConstant.False + + companion object { + fun valueOf(type: UNumericType) = when (type) { + UNumericType.DOUBLE -> Double + UNumericType.FLOAT -> Float + else -> throw AssertionError("NaN exists only for Float / Double, but not for $type") + } + } +} + +class UCharConstant(override val value: Char, override val source: ULiteralExpression? = null) : UAbstractConstant() { + override fun plus(other: UValue) = when (other) { + is UIntConstant -> UCharConstant(value + other.value) + is UCharConstant -> UCharConstant(value + other.value.toInt()) + else -> super.plus(other) + } + + override fun minus(other: UValue) = when (other) { + is UIntConstant -> UCharConstant(value - other.value) + is UCharConstant -> UIntConstant(value - other.value) + else -> super.plus(other) + } + + override fun greater(other: UValue) = when (other) { + is UCharConstant -> UBooleanConstant.valueOf(value > other.value) + else -> super.greater(other) + } + + override fun inc() = this + UIntConstant(1) + + override fun dec() = this - UIntConstant(1) + + override fun toString() = "\'$value\'" + + override fun asString() = "$value" +} + +sealed class UBooleanConstant(override val value: Boolean) : UAbstractConstant() { + override val source = null + + object True : UBooleanConstant(true) { + override fun not() = False + + override fun and(other: UValue) = other as? UBooleanConstant ?: super.and(other) + + override fun or(other: UValue) = True + } + + object False : UBooleanConstant(false) { + override fun not() = True + + override fun and(other: UValue) = False + + override fun or(other: UValue) = other as? UBooleanConstant ?: super.or(other) + } + + companion object { + fun valueOf(value: Boolean) = if (value) True else False + } +} + +class UStringConstant(override val value: String, override val source: ULiteralExpression? = null) : UAbstractConstant() { + + override fun plus(other: UValue) = when (other) { + is UConstant -> UStringConstant(value + other.asString()) + else -> super.plus(other) + } + + override fun greater(other: UValue) = when (other) { + is UStringConstant -> UBooleanConstant.valueOf(value > other.value) + else -> super.greater(other) + } + + override fun asString() = value + + override fun toString() = "\"$value\"" +} + +class UEnumEntryValueConstant(override val value: PsiEnumConstant, override val source: USimpleNameReferenceExpression? = null) : UAbstractConstant() { + override fun equals(other: Any?) = + other is UEnumEntryValueConstant && + value.nameIdentifier.text == other.value.nameIdentifier.text && + value.containingClass?.qualifiedName == other.value.containingClass?.qualifiedName + + override fun hashCode(): Int { + var result = 19 + result = result * 13 + value.nameIdentifier.text.hashCode() + result = result * 13 + (value.containingClass?.qualifiedName?.hashCode() ?: 0) + return result + } + + override fun toString() = value.name?.let { "$it (enum entry)" }?: "" + + override fun asString() = value.name ?: "" +} + +class UClassConstant(override val value: PsiType, override val source: UClassLiteralExpression? = null) : UAbstractConstant() { + override fun toString() = value.name +} + +object UNullConstant : UAbstractConstant() { + override val value = null + override val source = null +} diff --git a/uast/uast-common/src/org/jetbrains/uast/values/UDependency.kt b/uast/uast-common/src/org/jetbrains/uast/values/UDependency.kt new file mode 100644 index 000000000000..3d1a9a7a6ac8 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/values/UDependency.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.values + +interface UDependency { +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/values/UDependentValue.kt b/uast/uast-common/src/org/jetbrains/uast/values/UDependentValue.kt new file mode 100644 index 000000000000..4f9a6c08bf17 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/values/UDependentValue.kt @@ -0,0 +1,137 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.values + +open class UDependentValue protected constructor( + val value: UValue, + override val dependencies: Set = emptySet() +) : UValueBase() { + + private fun UValue.unwrap() = (this as? UDependentValue)?.unwrap() ?: this + + private fun unwrap(): UValue = value.unwrap() + + private val dependenciesWithThis: Set + get() = (this as? UDependency)?.let { dependencies + it } ?: dependencies + + private fun wrapBinary(result: UValue, arg: UValue): UValue { + val wrappedDependencies = (arg as? UDependentValue)?.dependenciesWithThis ?: emptySet() + val resultDependencies = dependenciesWithThis + wrappedDependencies + return create(result, resultDependencies) + } + + private fun wrapUnary(result: UValue) = create(result, dependenciesWithThis) + + override fun plus(other: UValue) = wrapBinary(unwrap() + other.unwrap(), other) + + override fun minus(other: UValue) = wrapBinary(unwrap() - other.unwrap(), other) + + override fun times(other: UValue) = wrapBinary(unwrap() * other.unwrap(), other) + + override fun div(other: UValue) = wrapBinary(unwrap() / other.unwrap(), other) + + internal fun inverseDiv(other: UValue) = wrapBinary(other.unwrap() / unwrap(), other) + + override fun mod(other: UValue) = wrapBinary(unwrap() % other.unwrap(), other) + + internal fun inverseMod(other: UValue) = wrapBinary(other.unwrap() % unwrap(), other) + + override fun unaryMinus() = wrapUnary(-unwrap()) + + override fun valueEquals(other: UValue) = wrapBinary(unwrap() valueEquals other.unwrap(), other) + + override fun valueNotEquals(other: UValue) = wrapBinary(unwrap() valueNotEquals other.unwrap(), other) + + override fun not() = wrapUnary(!unwrap()) + + override fun greater(other: UValue) = wrapBinary(unwrap() greater other.unwrap(), other) + + override fun less(other: UValue) = wrapBinary(other.unwrap() greater unwrap(), other) + + override fun inc() = wrapUnary(unwrap().inc()) + + override fun dec() = wrapUnary(unwrap().dec()) + + override fun and(other: UValue) = wrapBinary(unwrap() and other.unwrap(), other) + + override fun or(other: UValue) = wrapBinary(unwrap() or other.unwrap(), other) + + override fun bitwiseAnd(other: UValue) = wrapBinary(unwrap() bitwiseAnd other.unwrap(), other) + + override fun bitwiseOr(other: UValue) = wrapBinary(unwrap() bitwiseOr other.unwrap(), other) + + override fun bitwiseXor(other: UValue) = wrapBinary(unwrap() bitwiseXor other.unwrap(), other) + + override fun shl(other: UValue) = wrapBinary(unwrap() shl other.unwrap(), other) + + internal fun inverseShiftLeft(other: UValue) = wrapBinary(other.unwrap() shl unwrap(), other) + + override fun shr(other: UValue) = wrapBinary(unwrap() shr other.unwrap(), other) + + internal fun inverseShiftRight(other: UValue) = wrapBinary(other.unwrap() shr unwrap(), other) + + override fun ushr(other: UValue) = wrapBinary(unwrap() ushr other.unwrap(), other) + + internal fun inverseShiftRightUnsigned(other: UValue) = + wrapBinary(other.unwrap() ushr unwrap(), other) + + override fun merge(other: UValue) = when (other) { + this -> this + value -> this + is UDependentValue -> { + if (value != other.value) UPhiValue.create(this, other) + else UDependentValue(value, dependencies + other.dependencies) + } + else -> UPhiValue.create(this, other) + } + + override fun toConstant() = value.toConstant() + + open internal fun copy(dependencies: Set) = + if (dependencies == this.dependencies) this else create(value, dependencies) + + override fun coerceConstant(constant: UConstant): UValue = + if (toConstant() == constant) this + else create(value.coerceConstant(constant), dependencies) + + override fun equals(other: Any?) = + other is UDependentValue + && javaClass == other.javaClass + && value == other.value + && dependencies == other.dependencies + + override fun hashCode(): Int { + var result = 31 + result = result * 19 + value.hashCode() + result = result * 19 + dependencies.hashCode() + return result + } + + override fun toString() = + if (dependencies.isNotEmpty()) + "$value" + dependencies.joinToString(prefix = " (depending on: ", postfix = ")", separator = ", ") + else + "$value" + + companion object { + fun create(value: UValue, dependencies: Set): UValue = + if (dependencies.isNotEmpty()) UDependentValue(value, dependencies) + else value + + internal fun UValue.coerceConstant(constant: UConstant): UValue = + (this as? UValueBase)?.coerceConstant(constant) ?: constant + } +} diff --git a/uast/uast-common/src/org/jetbrains/uast/values/UNothingValue.kt b/uast/uast-common/src/org/jetbrains/uast/values/UNothingValue.kt new file mode 100644 index 000000000000..e966f752c51e --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/values/UNothingValue.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.values + +import org.jetbrains.uast.* + +// Something that never can be reached / created +internal class UNothingValue private constructor( + val containingLoopOrSwitch: UExpression?, + val kind: JumpKind +) : UValueBase() { + + constructor(jump: UJumpExpression) : this(jump.containingLoopOrSwitch(), jump.kind()) + + constructor() : this(null, JumpKind.OTHER) + + enum class JumpKind { + BREAK, + CONTINUE, + OTHER; + } + + override val reachable = false + + override fun merge(other: UValue) = when (other) { + is UNothingValue -> { + val mergedLoopOrSwitch = + if (containingLoopOrSwitch == other.containingLoopOrSwitch) containingLoopOrSwitch + else null + val mergedKind = if (mergedLoopOrSwitch == null || kind != other.kind) JumpKind.OTHER else kind + UNothingValue(mergedLoopOrSwitch, mergedKind) + } + else -> super.merge(other) + } + + override fun toString() = "Nothing" + when (kind) { + JumpKind.BREAK -> "(break)" + JumpKind.CONTINUE -> "(continue)" + else -> "" + } + + companion object { + private fun UJumpExpression.containingLoopOrSwitch(): UExpression? { + var containingElement = uastParent + while (containingElement != null) { + if (this is UBreakExpression && label == null && containingElement is USwitchExpression) { + return containingElement + } + if (containingElement is ULoopExpression) { + val containingLabeled = containingElement.uastParent as? ULabeledExpression + if (label == null || label == containingLabeled?.label) { + return containingElement + } + } + containingElement = containingElement.uastParent + } + return null + } + + private fun UExpression.kind(): JumpKind = when (this) { + is UBreakExpression -> JumpKind.BREAK + is UContinueExpression -> JumpKind.CONTINUE + else -> JumpKind.OTHER + } + } +} diff --git a/uast/uast-common/src/org/jetbrains/uast/values/UOperand.kt b/uast/uast-common/src/org/jetbrains/uast/values/UOperand.kt new file mode 100644 index 000000000000..9c94c8c4346a --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/values/UOperand.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.values + +interface UOperand { + operator fun plus(other: UValue): UValue + + operator fun minus(other: UValue): UValue + + operator fun times(other: UValue): UValue + + operator fun div(other: UValue): UValue + + operator fun mod(other: UValue): UValue + + operator fun unaryMinus(): UValue + + operator fun not(): UValue + + infix fun valueEquals(other: UValue): UValue + + infix fun valueNotEquals(other: UValue): UValue + + infix fun identityEquals(other: UValue): UValue + + infix fun identityNotEquals(other: UValue): UValue + + infix fun greater(other: UValue): UValue + + infix fun less(other: UValue): UValue + + infix fun greaterOrEquals(other: UValue): UValue + + infix fun lessOrEquals(other: UValue): UValue + + fun inc(): UValue + + fun dec(): UValue + + infix fun and(other: UValue): UValue + + infix fun or(other: UValue): UValue + + infix fun bitwiseAnd(other: UValue): UValue + + infix fun bitwiseOr(other: UValue): UValue + + infix fun bitwiseXor(other: UValue): UValue + + infix fun shl(other: UValue): UValue + + infix fun shr(other: UValue): UValue + + infix fun ushr(other: UValue): UValue +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/values/UPhiValue.kt b/uast/uast-common/src/org/jetbrains/uast/values/UPhiValue.kt new file mode 100644 index 000000000000..186505e07170 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/values/UPhiValue.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.values + +class UPhiValue private constructor(val values: Set): UValueBase() { + + override val dependencies: Set = values.flatMapTo(linkedSetOf()) { it.dependencies } + + override fun equals(other: Any?) = other is UPhiValue && values == other.values + + override fun hashCode() = values.hashCode() + + override fun toString() = values.joinToString(prefix = "Phi(", postfix = ")", separator = ", ") + + companion object { + fun create(values: Iterable): UPhiValue { + val flattenedValues = values.flatMapTo(linkedSetOf()) { (it as? UPhiValue)?.values ?: listOf(it) } + if (flattenedValues.size <= 1) { + throw AssertionError("UPhiValue should contain two or more values: $flattenedValues") + } + return UPhiValue(flattenedValues) + } + + fun create(vararg values: UValue) = create(values.asIterable()) + } +} + diff --git a/platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorGeneratedTextAttributesCache.java b/uast/uast-common/src/org/jetbrains/uast/values/UUndeterminedValue.kt similarity index 60% rename from platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorGeneratedTextAttributesCache.java rename to uast/uast-common/src/org/jetbrains/uast/values/UUndeterminedValue.kt index 6de929560e07..e900df99f7fe 100644 --- a/platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorGeneratedTextAttributesCache.java +++ b/uast/uast-common/src/org/jetbrains/uast/values/UUndeterminedValue.kt @@ -13,17 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.openapi.editor.colors; +package org.jetbrains.uast.values -import com.intellij.openapi.editor.markup.TextAttributes; -import org.jetbrains.annotations.Nullable; - -import java.util.Map; - -public interface EditorGeneratedTextAttributesCache { - @Nullable - default Map getGeneratedTextAttributesCache() { - return null; - } - default void dropGeneratedTextAttributesCache() {} +// Something with value that cannot be evaluated +object UUndeterminedValue : UValueBase() { + override fun toString() = "Undetermined" } + +fun UValue.ifUndetermined(block: () -> UValue) = if (this == UUndeterminedValue) block() else this diff --git a/uast/uast-common/src/org/jetbrains/uast/values/UValue.kt b/uast/uast-common/src/org/jetbrains/uast/values/UValue.kt new file mode 100644 index 000000000000..49cabf599489 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/values/UValue.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.values + +interface UValue : UOperand { + + fun merge(other: UValue): UValue + + val dependencies: Set + get() = emptySet() + + fun toConstant(): UConstant? + + val reachable: Boolean + + companion object { + val UNREACHABLE: UValue = UNothingValue() + } +} + +fun UValue.toPossibleConstants(): Set { + val results = mutableSetOf() + toPossibleConstants(results) + return results +} + +private fun UValue.toPossibleConstants(result: MutableSet) { + when (this) { + is UDependentValue -> value.toPossibleConstants(result) + is UPhiValue -> values.forEach { it.toPossibleConstants(result) } + else -> toConstant()?.let { result.add(it) } + } +} diff --git a/uast/uast-common/src/org/jetbrains/uast/values/UValueBase.kt b/uast/uast-common/src/org/jetbrains/uast/values/UValueBase.kt new file mode 100644 index 000000000000..c41b2800c1d2 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/values/UValueBase.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.values + +abstract class UValueBase : UValue { + + override operator fun plus(other: UValue): UValue = + if (other is UDependentValue) other + this else UUndeterminedValue + + override operator fun minus(other: UValue): UValue = this + (-other) + + override operator fun times(other: UValue): UValue = + if (other is UDependentValue) other * this else UUndeterminedValue + + override operator fun div(other: UValue): UValue = + (other as? UDependentValue)?.inverseDiv(this) ?: UUndeterminedValue + + override operator fun mod(other: UValue): UValue = + (other as? UDependentValue)?.inverseMod(this) ?: UUndeterminedValue + + override fun unaryMinus(): UValue = UUndeterminedValue + + override fun valueEquals(other: UValue): UValue = + if (other is UDependentValue || other is UNaNConstant) other.valueEquals(this) else UUndeterminedValue + + override fun valueNotEquals(other: UValue): UValue = !this.valueEquals(other) + + override fun identityEquals(other: UValue): UValue = valueEquals(other) + + override fun identityNotEquals(other: UValue): UValue = !this.identityEquals(other) + + override fun not(): UValue = UUndeterminedValue + + override fun greater(other: UValue): UValue = + if (other is UDependentValue || other is UNaNConstant) other.less(this) else UUndeterminedValue + + override fun less(other: UValue): UValue = other.greater(this) + + override fun greaterOrEquals(other: UValue) = this.greater(other) or this.valueEquals(other) + + override fun lessOrEquals(other: UValue) = this.less(other) or this.valueEquals(other) + + override fun inc(): UValue = UUndeterminedValue + + override fun dec(): UValue = UUndeterminedValue + + override fun and(other: UValue): UValue = + if (other is UDependentValue || other == UBooleanConstant.False) other and this else UUndeterminedValue + + override fun or(other: UValue): UValue = + if (other is UDependentValue || other == UBooleanConstant.True) other or this else UUndeterminedValue + + override fun bitwiseAnd(other: UValue): UValue = + if (other is UDependentValue) other bitwiseAnd this else UUndeterminedValue + + override fun bitwiseOr(other: UValue): UValue = + if (other is UDependentValue) other bitwiseOr this else UUndeterminedValue + + override fun bitwiseXor(other: UValue): UValue = + if (other is UDependentValue) other bitwiseXor this else UUndeterminedValue + + override fun shl(other: UValue): UValue = + (other as? UDependentValue)?.inverseShiftLeft(this) ?: UUndeterminedValue + + override fun shr(other: UValue): UValue = + (other as? UDependentValue)?.inverseShiftRight(this) ?: UUndeterminedValue + + override fun ushr(other: UValue): UValue = + (other as? UDependentValue)?.inverseShiftRightUnsigned(this) ?: UUndeterminedValue + + override fun merge(other: UValue): UValue = when (other) { + this -> this + is UVariableValue -> other.merge(this) + else -> UPhiValue.create(this, other) + } + + override val dependencies: Set + get() = emptySet() + + override fun toConstant(): UConstant? = this as? UConstant + + internal open fun coerceConstant(constant: UConstant): UValue = constant + + override val reachable = true + + override abstract fun toString(): String +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/values/UVariableValue.kt b/uast/uast-common/src/org/jetbrains/uast/values/UVariableValue.kt new file mode 100644 index 000000000000..2185cd9e38ed --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/values/UVariableValue.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.values + +import com.intellij.psi.PsiType +import org.jetbrains.uast.UVariable + +class UVariableValue private constructor( + val variable: UVariable, + value: UValue, + dependencies: Set +) : UDependentValue(value, dependencies), UDependency { + + override fun identityEquals(other: UValue): UValue = + if (this == other) super.valueEquals(other) + else when (variable.psi.type) { + PsiType.BYTE, PsiType.FLOAT, PsiType.DOUBLE, PsiType.LONG, + PsiType.SHORT, PsiType.INT, PsiType.CHAR, PsiType.BOOLEAN -> super.valueEquals(other) + + else -> UUndeterminedValue + } + + override fun merge(other: UValue) = when (other) { + this -> this + value -> this + is UVariableValue -> { + if (variable != other.variable || value != other.value) UPhiValue.create(this, other) + else create(variable, value, dependencies + other.dependencies) + } + is UDependentValue -> { + if (value != other.value) UPhiValue.create(this, other) + else create(variable, value, dependencies + other.dependencies) + } + else -> UPhiValue.create(this, other) + } + + override fun copy(dependencies: Set) = + if (dependencies == this.dependencies) this else create(variable, value, dependencies) + + override fun coerceConstant(constant: UConstant): UValue = + if (constant == toConstant()) this + else create(variable, value.coerceConstant(constant), dependencies) + + override fun equals(other: Any?) = + other is UVariableValue + && variable == other.variable + && value == other.value + && dependencies == other.dependencies + + override fun hashCode(): Int { + var result = 31 + result = result * 19 + variable.hashCode() + result = result * 19 + value.hashCode() + result = result * 19 + dependencies.hashCode() + return result + } + + override fun toString() = "(var ${variable.name ?: ""} = ${super.toString()})" + + companion object { + + private fun Set.filterNot(variable: UVariable) = + filterTo(linkedSetOf()) { it !is UVariableValue || variable != it.variable } + + fun create(variable: UVariable, value: UValue, dependencies: Set = emptySet()): UVariableValue { + when (variable.psi.type) { + PsiType.BYTE, PsiType.SHORT -> { + val constant = value.toConstant() + if (constant is UIntConstant && constant.type == UNumericType.INT) { + val castConstant = UIntConstant(constant.value, variable.psi.type) + return create(variable, value.coerceConstant(castConstant), dependencies) + } + } + } + val dependenciesWithoutSelf = dependencies.filterNot(variable) + return when { + value is UVariableValue + && variable == value.variable + && dependenciesWithoutSelf == value.dependencies -> value + + value is UDependentValue -> { + val valueDependencies = value.dependencies.filterNot(variable) + val modifiedValue = value.copy(valueDependencies) + UVariableValue(variable, modifiedValue, dependenciesWithoutSelf) + } + + else -> UVariableValue(variable, value, dependenciesWithoutSelf) + } + } + } +} diff --git a/uast/uast-common/src/org/jetbrains/uast/visitor/DelegatingUastVisitor.kt b/uast/uast-common/src/org/jetbrains/uast/visitor/DelegatingUastVisitor.kt new file mode 100644 index 000000000000..35544f9a0d74 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/visitor/DelegatingUastVisitor.kt @@ -0,0 +1,174 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.visitor + +import org.jetbrains.uast.* + + +class DelegatingUastVisitor(private val visitors: List): UastVisitor { + override fun visitElement(node: UElement): Boolean { + return visitors.all { it.visitElement(node) } + } + + override fun visitVariable(node: UVariable): Boolean { + return visitors.all { it.visitVariable(node) } + } + + override fun visitMethod(node: UMethod): Boolean { + return visitors.all { it.visitMethod(node) } + } + + override fun visitLabeledExpression(node: ULabeledExpression): Boolean { + return visitors.all { it.visitLabeledExpression(node) } + } + + override fun visitDeclarationsExpression(node: UDeclarationsExpression): Boolean { + return visitors.all { it.visitDeclarationsExpression(node) } + } + + override fun visitBlockExpression(node: UBlockExpression): Boolean { + return visitors.all { it.visitBlockExpression(node) } + } + + override fun visitQualifiedReferenceExpression(node: UQualifiedReferenceExpression): Boolean { + return visitors.all { it.visitQualifiedReferenceExpression(node) } + } + + override fun visitSimpleNameReferenceExpression(node: USimpleNameReferenceExpression): Boolean { + return visitors.all { it.visitSimpleNameReferenceExpression(node) } + } + + override fun visitTypeReferenceExpression(node: UTypeReferenceExpression): Boolean { + return visitors.all { it.visitTypeReferenceExpression(node) } + } + + override fun visitCallExpression(node: UCallExpression): Boolean { + return visitors.all { it.visitCallExpression(node) } + } + + override fun visitBinaryExpression(node: UBinaryExpression): Boolean { + return visitors.all { it.visitBinaryExpression(node) } + } + + override fun visitBinaryExpressionWithType(node: UBinaryExpressionWithType): Boolean { + return visitors.all { it.visitBinaryExpressionWithType(node) } + } + + override fun visitParenthesizedExpression(node: UParenthesizedExpression): Boolean { + return visitors.all { it.visitParenthesizedExpression(node) } + } + + override fun visitUnaryExpression(node: UUnaryExpression): Boolean { + return visitors.all { it.visitUnaryExpression(node) } + } + + override fun visitPrefixExpression(node: UPrefixExpression): Boolean { + return visitors.all { it.visitPrefixExpression(node) } + } + + override fun visitPostfixExpression(node: UPostfixExpression): Boolean { + return visitors.all { it.visitPostfixExpression(node) } + } + + override fun visitExpressionList(node: UExpressionList): Boolean { + return visitors.all { it.visitExpressionList(node) } + } + + override fun visitIfExpression(node: UIfExpression): Boolean { + return visitors.all { it.visitIfExpression(node) } + } + + override fun visitSwitchExpression(node: USwitchExpression): Boolean { + return visitors.all { it.visitSwitchExpression(node) } + } + + override fun visitSwitchClauseExpression(node: USwitchClauseExpression): Boolean { + return visitors.all { it.visitSwitchClauseExpression(node) } + } + + override fun visitWhileExpression(node: UWhileExpression): Boolean { + return visitors.all { it.visitWhileExpression(node) } + } + + override fun visitDoWhileExpression(node: UDoWhileExpression): Boolean { + return visitors.all { it.visitDoWhileExpression(node) } + } + + override fun visitForExpression(node: UForExpression): Boolean { + return visitors.all { it.visitForExpression(node) } + } + + override fun visitForEachExpression(node: UForEachExpression): Boolean { + return visitors.all { it.visitForEachExpression(node) } + } + + override fun visitTryExpression(node: UTryExpression): Boolean { + return visitors.all { it.visitTryExpression(node) } + } + + override fun visitCatchClause(node: UCatchClause): Boolean { + return visitors.all { it.visitCatchClause(node) } + } + + override fun visitLiteralExpression(node: ULiteralExpression): Boolean { + return visitors.all { it.visitLiteralExpression(node) } + } + + override fun visitThisExpression(node: UThisExpression): Boolean { + return visitors.all { it.visitThisExpression(node) } + } + + override fun visitSuperExpression(node: USuperExpression): Boolean { + return visitors.all { it.visitSuperExpression(node) } + } + + override fun visitReturnExpression(node: UReturnExpression): Boolean { + return visitors.all { it.visitReturnExpression(node) } + } + + override fun visitBreakExpression(node: UBreakExpression): Boolean { + return visitors.all { it.visitBreakExpression(node) } + } + + override fun visitContinueExpression(node: UContinueExpression): Boolean { + return visitors.all { it.visitContinueExpression(node) } + } + + override fun visitThrowExpression(node: UThrowExpression): Boolean { + return visitors.all { it.visitThrowExpression(node) } + } + + override fun visitArrayAccessExpression(node: UArrayAccessExpression): Boolean { + return visitors.all { it.visitArrayAccessExpression(node) } + } + + override fun visitCallableReferenceExpression(node: UCallableReferenceExpression): Boolean { + return visitors.all { it.visitCallableReferenceExpression(node) } + } + + override fun visitClassLiteralExpression(node: UClassLiteralExpression): Boolean { + return visitors.all { it.visitClassLiteralExpression(node) } + } + + override fun visitLambdaExpression(node: ULambdaExpression): Boolean { + return visitors.all { it.visitLambdaExpression(node) } + } + + override fun visitObjectLiteralExpression(node: UObjectLiteralExpression): Boolean { + return visitors.all { it.visitObjectLiteralExpression(node) } + } +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/visitor/UastTypedVisitor.kt b/uast/uast-common/src/org/jetbrains/uast/visitor/UastTypedVisitor.kt new file mode 100644 index 000000000000..f4cf5305aa05 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/visitor/UastTypedVisitor.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.visitor + +import org.jetbrains.uast.* + +interface UastTypedVisitor { + fun visitElement(node: UElement, data: D): R + // Just elements + fun visitFile(node: UFile, data: D): R = visitElement(node, data) + fun visitImportStatement(node: UImportStatement, data: D): R = visitElement(node, data) + fun visitAnnotation(node: UAnnotation, data: D): R = visitElement(node, data) + fun visitCatchClause(node: UCatchClause, data: D) = visitElement(node, data) + // Declarations + fun visitDeclaration(node: UDeclaration, data: D) = visitElement(node, data) + fun visitClass(node: UClass, data: D): R = visitDeclaration(node, data) + fun visitMethod(node: UMethod, data: D): R = visitDeclaration(node, data) + fun visitClassInitializer(node: UClassInitializer, data: D): R = visitDeclaration(node, data) + // Variables + fun visitVariable(node: UVariable, data: D): R = visitDeclaration(node, data) + fun visitParameter(node: UParameter, data: D): R = visitVariable(node, data) + fun visitField(node: UField, data: D): R = visitVariable(node, data) + fun visitLocalVariable(node: ULocalVariable, data: D): R = visitVariable(node, data) + fun visitEnumConstantExpression(node: UEnumConstant, data: D) = visitVariable(node, data) + // Expressions + fun visitExpression(node: UExpression, data: D) = visitElement(node, data) + fun visitLabeledExpression(node: ULabeledExpression, data: D) = visitExpression(node, data) + fun visitDeclarationsExpression(node: UDeclarationsExpression, data: D) = visitExpression(node, data) + fun visitBlockExpression(node: UBlockExpression, data: D) = visitExpression(node, data) + fun visitTypeReferenceExpression(node: UTypeReferenceExpression, data: D) = visitExpression(node, data) + fun visitExpressionList(node: UExpressionList, data: D) = visitExpression(node, data) + fun visitLiteralExpression(node: ULiteralExpression, data: D) = visitExpression(node, data) + fun visitThisExpression(node: UThisExpression, data: D) = visitExpression(node, data) + fun visitSuperExpression(node: USuperExpression, data: D) = visitExpression(node, data) + fun visitArrayAccessExpression(node: UArrayAccessExpression, data: D) = visitExpression(node, data) + fun visitClassLiteralExpression(node: UClassLiteralExpression, data: D) = visitExpression(node, data) + fun visitLambdaExpression(node: ULambdaExpression, data: D) = visitExpression(node, data) + fun visitPolyadicExpression(node: UPolyadicExpression, data: D) = visitExpression(node, data) + // Calls + fun visitCallExpression(node: UCallExpression, data: D) = visitExpression(node, data) + fun visitObjectLiteralExpression(node: UObjectLiteralExpression, data: D) = visitCallExpression(node, data) + // Operations + fun visitBinaryExpression(node: UBinaryExpression, data: D) = visitPolyadicExpression(node, data) + fun visitBinaryExpressionWithType(node: UBinaryExpressionWithType, data: D) = visitExpression(node, data) + fun visitParenthesizedExpression(node: UParenthesizedExpression, data: D) = visitExpression(node, data) + // Unary operations + fun visitUnaryExpression(node: UUnaryExpression, data: D) = visitExpression(node, data) + fun visitPrefixExpression(node: UPrefixExpression, data: D) = visitUnaryExpression(node, data) + fun visitPostfixExpression(node: UPostfixExpression, data: D) = visitUnaryExpression(node, data) + // References + fun visitReferenceExpression(node: UReferenceExpression, data: D) = visitExpression(node, data) + fun visitQualifiedReferenceExpression(node: UQualifiedReferenceExpression, data: D) = visitReferenceExpression(node, data) + fun visitSimpleNameReferenceExpression(node: USimpleNameReferenceExpression, data: D) = visitReferenceExpression(node, data) + fun visitCallableReferenceExpression(node: UCallableReferenceExpression, data: D) = visitReferenceExpression(node, data) + // Control structures + fun visitIfExpression(node: UIfExpression, data: D) = visitExpression(node, data) + fun visitSwitchExpression(node: USwitchExpression, data: D) = visitExpression(node, data) + fun visitSwitchClauseExpression(node: USwitchClauseExpression, data: D) = visitExpression(node, data) + fun visitTryExpression(node: UTryExpression, data: D) = visitExpression(node, data) + // Jumps + fun visitReturnExpression(node: UReturnExpression, data: D) = visitExpression(node, data) + fun visitBreakExpression(node: UBreakExpression, data: D) = visitExpression(node, data) + fun visitContinueExpression(node: UContinueExpression, data: D) = visitExpression(node, data) + fun visitThrowExpression(node: UThrowExpression, data: D) = visitExpression(node, data) + // Loops + fun visitLoopExpression(node: ULoopExpression, data: D) = visitExpression(node, data) + fun visitWhileExpression(node: UWhileExpression, data: D) = visitLoopExpression(node, data) + fun visitDoWhileExpression(node: UDoWhileExpression, data: D) = visitLoopExpression(node, data) + fun visitForExpression(node: UForExpression, data: D) = visitLoopExpression(node, data) + fun visitForEachExpression(node: UForEachExpression, data: D) = visitLoopExpression(node, data) +} \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/visitor/UastVisitor.kt b/uast/uast-common/src/org/jetbrains/uast/visitor/UastVisitor.kt new file mode 100644 index 000000000000..7e2b61071132 --- /dev/null +++ b/uast/uast-common/src/org/jetbrains/uast/visitor/UastVisitor.kt @@ -0,0 +1,134 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.visitor + +import org.jetbrains.uast.* + +interface UastVisitor { + fun visitElement(node: UElement): Boolean + + fun visitFile(node: UFile): Boolean = visitElement(node) + fun visitImportStatement(node: UImportStatement): Boolean = visitElement(node) + fun visitClass(node: UClass): Boolean = visitElement(node) + fun visitInitializer(node: UClassInitializer): Boolean = visitElement(node) + fun visitMethod(node: UMethod): Boolean = visitElement(node) + fun visitVariable(node: UVariable): Boolean = visitElement(node) + fun visitParameter(node: UParameter): Boolean = visitVariable(node) + fun visitField(node: UField): Boolean = visitVariable(node) + fun visitLocalVariable(node: ULocalVariable): Boolean = visitVariable(node) + fun visitEnumConstant(node: UEnumConstant): Boolean = visitField(node) + + fun visitAnnotation(node: UAnnotation): Boolean = visitElement(node) + + // Expressions + fun visitLabeledExpression(node: ULabeledExpression) = visitElement(node) + fun visitDeclarationsExpression(node: UDeclarationsExpression) = visitElement(node) + fun visitBlockExpression(node: UBlockExpression) = visitElement(node) + fun visitQualifiedReferenceExpression(node: UQualifiedReferenceExpression) = visitElement(node) + fun visitSimpleNameReferenceExpression(node: USimpleNameReferenceExpression) = visitElement(node) + fun visitTypeReferenceExpression(node: UTypeReferenceExpression) = visitElement(node) + fun visitCallExpression(node: UCallExpression) = visitElement(node) + fun visitBinaryExpression(node: UBinaryExpression) = visitElement(node) + fun visitBinaryExpressionWithType(node: UBinaryExpressionWithType) = visitElement(node) + fun visitPolyadicExpression(node: UPolyadicExpression) = visitElement(node) + fun visitParenthesizedExpression(node: UParenthesizedExpression) = visitElement(node) + fun visitUnaryExpression(node: UUnaryExpression) = visitElement(node) + fun visitPrefixExpression(node: UPrefixExpression) = visitElement(node) + fun visitPostfixExpression(node: UPostfixExpression) = visitElement(node) + fun visitExpressionList(node: UExpressionList) = visitElement(node) + fun visitIfExpression(node: UIfExpression) = visitElement(node) + fun visitSwitchExpression(node: USwitchExpression) = visitElement(node) + fun visitSwitchClauseExpression(node: USwitchClauseExpression) = visitElement(node) + fun visitWhileExpression(node: UWhileExpression) = visitElement(node) + fun visitDoWhileExpression(node: UDoWhileExpression) = visitElement(node) + fun visitForExpression(node: UForExpression) = visitElement(node) + fun visitForEachExpression(node: UForEachExpression) = visitElement(node) + fun visitTryExpression(node: UTryExpression) = visitElement(node) + fun visitCatchClause(node: UCatchClause) = visitElement(node) + fun visitLiteralExpression(node: ULiteralExpression) = visitElement(node) + fun visitThisExpression(node: UThisExpression) = visitElement(node) + fun visitSuperExpression(node: USuperExpression) = visitElement(node) + fun visitReturnExpression(node: UReturnExpression) = visitElement(node) + fun visitBreakExpression(node: UBreakExpression) = visitElement(node) + fun visitContinueExpression(node: UContinueExpression) = visitElement(node) + fun visitThrowExpression(node: UThrowExpression) = visitElement(node) + fun visitArrayAccessExpression(node: UArrayAccessExpression) = visitElement(node) + fun visitCallableReferenceExpression(node: UCallableReferenceExpression) = visitElement(node) + fun visitClassLiteralExpression(node: UClassLiteralExpression) = visitElement(node) + fun visitLambdaExpression(node: ULambdaExpression) = visitElement(node) + fun visitObjectLiteralExpression(node: UObjectLiteralExpression) = visitElement(node) + + // After + + fun afterVisitElement(node: UElement) {} + + fun afterVisitFile(node: UFile) { afterVisitElement(node) } + fun afterVisitImportStatement(node: UImportStatement) { afterVisitElement(node) } + fun afterVisitClass(node: UClass) { afterVisitElement(node) } + fun afterVisitInitializer(node: UClassInitializer) { afterVisitElement(node) } + fun afterVisitMethod(node: UMethod) { afterVisitElement(node) } + fun afterVisitVariable(node: UVariable) { afterVisitElement(node) } + fun afterVisitParameter(node: UParameter){ afterVisitVariable(node) } + fun afterVisitField(node: UField){ afterVisitVariable(node) } + fun afterVisitLocalVariable(node: ULocalVariable){ afterVisitVariable(node) } + fun afterVisitEnumConstant(node: UEnumConstant){ afterVisitField(node) } + fun afterVisitAnnotation(node: UAnnotation) { afterVisitElement(node) } + + // Expressions + fun afterVisitLabeledExpression(node: ULabeledExpression) { afterVisitElement(node) } + fun afterVisitDeclarationsExpression(node: UDeclarationsExpression) { afterVisitElement(node) } + fun afterVisitBlockExpression(node: UBlockExpression) { afterVisitElement(node) } + fun afterVisitQualifiedReferenceExpression(node: UQualifiedReferenceExpression) { afterVisitElement(node) } + fun afterVisitSimpleNameReferenceExpression(node: USimpleNameReferenceExpression) { afterVisitElement(node) } + fun afterVisitTypeReferenceExpression(node: UTypeReferenceExpression) { afterVisitElement(node) } + fun afterVisitCallExpression(node: UCallExpression) { afterVisitElement(node) } + fun afterVisitBinaryExpression(node: UBinaryExpression) { afterVisitElement(node) } + fun afterVisitBinaryExpressionWithType(node: UBinaryExpressionWithType) { afterVisitElement(node) } + fun afterVisitParenthesizedExpression(node: UParenthesizedExpression) { afterVisitElement(node) } + fun afterVisitUnaryExpression(node: UUnaryExpression) { afterVisitElement(node) } + fun afterVisitPrefixExpression(node: UPrefixExpression) { afterVisitElement(node) } + fun afterVisitPostfixExpression(node: UPostfixExpression) { afterVisitElement(node) } + fun afterVisitExpressionList(node: UExpressionList) { afterVisitElement(node) } + fun afterVisitIfExpression(node: UIfExpression) { afterVisitElement(node) } + fun afterVisitSwitchExpression(node: USwitchExpression) { afterVisitElement(node) } + fun afterVisitSwitchClauseExpression(node: USwitchClauseExpression) { afterVisitElement(node) } + fun afterVisitWhileExpression(node: UWhileExpression) { afterVisitElement(node) } + fun afterVisitDoWhileExpression(node: UDoWhileExpression) { afterVisitElement(node) } + fun afterVisitForExpression(node: UForExpression) { afterVisitElement(node) } + fun afterVisitForEachExpression(node: UForEachExpression) { afterVisitElement(node) } + fun afterVisitTryExpression(node: UTryExpression) { afterVisitElement(node) } + fun afterVisitCatchClause(node: UCatchClause) { afterVisitElement(node) } + fun afterVisitLiteralExpression(node: ULiteralExpression) { afterVisitElement(node) } + fun afterVisitThisExpression(node: UThisExpression) { afterVisitElement(node) } + fun afterVisitSuperExpression(node: USuperExpression) { afterVisitElement(node) } + fun afterVisitReturnExpression(node: UReturnExpression) { afterVisitElement(node) } + fun afterVisitBreakExpression(node: UBreakExpression) { afterVisitElement(node) } + fun afterVisitContinueExpression(node: UContinueExpression) { afterVisitElement(node) } + fun afterVisitThrowExpression(node: UThrowExpression) { afterVisitElement(node) } + fun afterVisitArrayAccessExpression(node: UArrayAccessExpression) { afterVisitElement(node) } + fun afterVisitCallableReferenceExpression(node: UCallableReferenceExpression) { afterVisitElement(node) } + fun afterVisitClassLiteralExpression(node: UClassLiteralExpression) { afterVisitElement(node) } + fun afterVisitLambdaExpression(node: ULambdaExpression) { afterVisitElement(node) } + fun afterVisitObjectLiteralExpression(node: UObjectLiteralExpression) { afterVisitElement(node) } + fun afterVisitPolyadicExpression(node: UPolyadicExpression) { afterVisitElement(node) } +} + +abstract class AbstractUastVisitor : UastVisitor { + override fun visitElement(node: UElement): Boolean = false + +} + +object EmptyUastVisitor : AbstractUastVisitor() \ No newline at end of file diff --git a/community-tests/community-tests.iml b/uast/uast-common/uast-common.iml similarity index 65% rename from community-tests/community-tests.iml rename to uast/uast-common/uast-common.iml index ee1899fd5ff9..3b903da78f6f 100644 --- a/community-tests/community-tests.iml +++ b/uast/uast-common/uast-common.iml @@ -3,9 +3,12 @@ - + + + + \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/JavaAbstractUElement.kt b/uast/uast-java/src/org/jetbrains/uast/java/JavaAbstractUElement.kt new file mode 100644 index 000000000000..30c5a1d272b2 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/JavaAbstractUElement.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.java + +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiExpression +import com.intellij.psi.PsiType +import org.jetbrains.uast.UAnnotation +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UExpression +import org.jetbrains.uast.java.internal.JavaUElementWithComments + +abstract class JavaAbstractUElement : JavaUElementWithComments { + override fun equals(other: Any?): Boolean { + if (other !is UElement || other.javaClass != this.javaClass) return false + return this.psi == other.psi + } + + override fun asSourceString(): String { + return this.psi?.text ?: super.asSourceString() + } + + override fun toString() = asRenderString() +} + +abstract class JavaAbstractUExpression : JavaAbstractUElement(), UExpression { + override fun evaluate(): Any? { + val project = psi?.project ?: return null + return JavaPsiFacade.getInstance(project).constantEvaluationHelper.computeConstantExpression(psi) + } + + override val annotations: List + get() = emptyList() + + override fun getExpressionType(): PsiType? { + val expression = psi as? PsiExpression ?: return null + return expression.type + } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/JavaUastLanguagePlugin.kt b/uast/uast-java/src/org/jetbrains/uast/java/JavaUastLanguagePlugin.kt new file mode 100644 index 000000000000..46c9652380ca --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/JavaUastLanguagePlugin.kt @@ -0,0 +1,334 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.java + +import com.intellij.lang.Language +import com.intellij.lang.java.JavaLanguage +import com.intellij.psi.* +import org.jetbrains.uast.* +import org.jetbrains.uast.java.expressions.JavaUNamedExpression +import org.jetbrains.uast.java.expressions.JavaUSynchronizedExpression + +class JavaUastLanguagePlugin : UastLanguagePlugin { + override val priority = 0 + + override fun isFileSupported(fileName: String) = fileName.endsWith(".java", ignoreCase = true) + + override val language: Language + get() = JavaLanguage.INSTANCE + + override fun isExpressionValueUsed(element: UExpression): Boolean = when (element) { + is JavaUDeclarationsExpression -> false + is UnknownJavaExpression -> (element.uastParent as? UExpression)?.let { isExpressionValueUsed(it) } ?: false + else -> { + val statement = element.psi as? PsiStatement + statement != null && statement.parent !is PsiExpressionStatement + } + } + + override fun getMethodCallExpression( + element: PsiElement, + containingClassFqName: String?, + methodName: String + ): UastLanguagePlugin.ResolvedMethod? { + if (element !is PsiMethodCallExpression) return null + if (element.methodExpression.referenceName != methodName) return null + + val uElement = convertElementWithParent(element, null) + val callExpression = when (uElement) { + is UCallExpression -> uElement + is UQualifiedReferenceExpression -> uElement.selector as UCallExpression + else -> error("Invalid element type: $uElement") + } + + val method = callExpression.resolve() ?: return null + if (containingClassFqName != null) { + val containingClass = method.containingClass ?: return null + if (containingClass.qualifiedName != containingClassFqName) return null + } + + return UastLanguagePlugin.ResolvedMethod(callExpression, method) + } + + override fun getConstructorCallExpression( + element: PsiElement, + fqName: String + ): UastLanguagePlugin.ResolvedConstructor? { + if (element !is PsiNewExpression) return null + val simpleName = fqName.substringAfterLast('.') + if (element.classReference?.referenceName != simpleName) return null + + val callExpression = convertElementWithParent(element, null) as? UCallExpression ?: return null + + val constructorMethod = element.resolveConstructor() ?: return null + val containingClass = constructorMethod.containingClass ?: return null + if (containingClass.qualifiedName != fqName) return null + + return UastLanguagePlugin.ResolvedConstructor(callExpression, constructorMethod, containingClass) + } + + override fun convertElement(element: PsiElement, parent: UElement?, requiredType: Class?): UElement? { + if (element !is PsiElement) return null + val parentCallback = parent.toCallback() + return convertDeclaration(element, parentCallback, requiredType) ?: + JavaConverter.convertPsiElement(element, parentCallback, requiredType) + } + + override fun convertElementWithParent(element: PsiElement, requiredType: Class?): UElement? { + if (element !is PsiElement) return null + if (element is PsiJavaFile) return requiredType.el { JavaUFile(element, this) } + JavaConverter.getCached(element)?.let { return it } + + val parentCallback = fun(): UElement? { + val parent = JavaConverter.unwrapElements(element.parent) ?: return null + return convertElementWithParent(parent, null) ?: return null + } + return convertDeclaration(element, parentCallback, requiredType) ?: + JavaConverter.convertPsiElement(element, parentCallback, requiredType) + } + + private fun convertDeclaration(element: PsiElement, + parentCallback: (() -> UElement?)?, + requiredType: Class?): UElement? { + fun

build(ctor: (P, UElement?) -> UElement): () -> UElement? { + return fun(): UElement? { + val parent = if (parentCallback == null) null else (parentCallback() ?: return null) + return ctor(element as P, parent) + } + } + + if (element.isValid) element.getUserData(JAVA_CACHED_UELEMENT_KEY)?.let { ref -> + ref.get()?.let { return it } + } + + return with (requiredType) { when (element) { + is PsiJavaFile -> el { JavaUFile(element, this@JavaUastLanguagePlugin) } + is UDeclaration -> el { element } + is PsiClass -> el { + val parent = if (parentCallback == null) null else (parentCallback() ?: return null) + JavaUClass.create(element, parent) + } + is PsiMethod -> el { + val parent = if (parentCallback == null) null else (parentCallback() ?: return null) + JavaUMethod.create(element, this@JavaUastLanguagePlugin, parent) + } + is PsiClassInitializer -> el(build(::JavaUClassInitializer)) + is PsiEnumConstant -> el(build(::JavaUEnumConstant)) + is PsiLocalVariable -> el(build(::JavaULocalVariable)) + is PsiParameter -> el(build(::JavaUParameter)) + is PsiField -> el(build(::JavaUField)) + is PsiVariable -> el(build(::JavaUVariable)) + is PsiAnnotation -> el(build(::JavaUAnnotation)) + else -> null + }} + } +} + +internal inline fun Class?.el(f: () -> UElement?): UElement? { + return if (this == null || isAssignableFrom(ActualT::class.java)) f() else null +} + +internal inline fun Class?.expr(f: () -> UExpression?): UExpression? { + return if (this == null || isAssignableFrom(ActualT::class.java)) f() else null +} + +private fun UElement?.toCallback() = if (this != null) fun(): UElement? { return this } else null + +internal object JavaConverter { + internal inline fun getCached(element: PsiElement): T? { + return null + //todo + } + + internal tailrec fun unwrapElements(element: PsiElement?): PsiElement? = when (element) { + is PsiExpressionStatement -> unwrapElements(element.parent) + is PsiParameterList -> unwrapElements(element.parent) + is PsiAnnotationParameterList -> unwrapElements(element.parent) + else -> element + } + + internal fun convertPsiElement(el: PsiElement, + parentCallback: (() -> UElement?)?, + requiredType: Class? = null): UElement? { + getCached(el)?.let { return it } + + fun

build(ctor: (P, UElement?) -> UElement): () -> UElement? { + return fun(): UElement? { + val parent = if (parentCallback == null) null else (parentCallback() ?: return null) + return ctor(el as P, parent) + } + } + + return with (requiredType) { when (el) { + is PsiCodeBlock -> el(build(::JavaUCodeBlockExpression)) + is PsiResourceExpression -> convertExpression(el.expression, parentCallback, requiredType) + is PsiExpression -> convertExpression(el, parentCallback, requiredType) + is PsiStatement -> convertStatement(el, parentCallback, requiredType) + is PsiIdentifier -> el { + val parent = if (parentCallback == null) null else (parentCallback() ?: return null) + JavaUSimpleNameReferenceExpression(el, el.text, parent) + } + is PsiNameValuePair -> el(build(::JavaUNamedExpression)) + is PsiArrayInitializerMemberValue -> el(build(::JavaAnnotationArrayInitializerUCallExpression)) + is PsiTypeElement -> el(build(::JavaUTypeReferenceExpression)) + is PsiJavaCodeReferenceElement -> convertReference(el, parentCallback, requiredType) + else -> null + }} + } + + internal fun convertBlock(block: PsiCodeBlock, parent: UElement?): UBlockExpression = + getCached(block) ?: JavaUCodeBlockExpression(block, parent) + + internal fun convertReference(reference: PsiJavaCodeReferenceElement, parentCallback: (() -> UElement?)?, requiredType: Class?): UExpression? { + return with (requiredType) { + val parent = if (parentCallback == null) null else (parentCallback() ?: return null) + if (reference.isQualified) { + expr { JavaUQualifiedReferenceExpression(reference, parent) } + } else { + val name = reference.referenceName ?: "" + expr { JavaUSimpleNameReferenceExpression(reference, name, parent, reference) } + } + } + } + + internal fun convertExpression(el: PsiExpression, + parentCallback: (() -> UElement?)?, + requiredType: Class? = null): UExpression? { + getCached(el)?.let { return it } + + fun

build(ctor: (P, UElement?) -> UExpression): () -> UExpression? { + return fun(): UExpression? { + val parent = if (parentCallback == null) null else (parentCallback() ?: return null) + return ctor(el as P, parent) + } + } + + return with (requiredType) { when (el) { + is PsiAssignmentExpression -> expr(build(::JavaUAssignmentExpression)) + is PsiConditionalExpression -> expr(build(::JavaUTernaryIfExpression)) + is PsiNewExpression -> { + if (el.anonymousClass != null) + expr(build(::JavaUObjectLiteralExpression)) + else + expr(build(::JavaConstructorUCallExpression)) + } + is PsiMethodCallExpression -> { + if (el.methodExpression.qualifierExpression != null) + expr { + val parent = if (parentCallback == null) null else (parentCallback() ?: return null) + JavaUCompositeQualifiedExpression(el, parent).apply { + receiver = convertOrEmpty(el.methodExpression.qualifierExpression!!, this) + selector = JavaUCallExpression(el, this) + } + } + else + expr(build(::JavaUCallExpression)) + } + is PsiArrayInitializerExpression -> expr(build(::JavaArrayInitializerUCallExpression)) + is PsiBinaryExpression -> expr(build(::JavaUBinaryExpression)) + // Should go after PsiBinaryExpression since it implements PsiPolyadicExpression + is PsiPolyadicExpression -> expr(build(::JavaUPolyadicExpression)) + is PsiParenthesizedExpression -> expr(build(::JavaUParenthesizedExpression)) + is PsiPrefixExpression -> expr(build(::JavaUPrefixExpression)) + is PsiPostfixExpression -> expr(build(::JavaUPostfixExpression)) + is PsiLiteralExpression -> expr(build(::JavaULiteralExpression)) + is PsiMethodReferenceExpression -> expr(build(::JavaUCallableReferenceExpression)) + is PsiReferenceExpression -> convertReference(el, parentCallback, requiredType) + is PsiThisExpression -> expr(build(::JavaUThisExpression)) + is PsiSuperExpression -> expr(build(::JavaUSuperExpression)) + is PsiInstanceOfExpression -> expr(build(::JavaUInstanceCheckExpression)) + is PsiTypeCastExpression -> expr(build(::JavaUTypeCastExpression)) + is PsiClassObjectAccessExpression -> expr(build(::JavaUClassLiteralExpression)) + is PsiArrayAccessExpression -> expr(build(::JavaUArrayAccessExpression)) + is PsiLambdaExpression -> expr(build(::JavaULambdaExpression)) + else -> expr(build(::UnknownJavaExpression)) + }} + } + + internal fun convertStatement(el: PsiStatement, + parentCallback: (() -> UElement?)?, + requiredType: Class? = null): UExpression? { + getCached(el)?.let { return it } + + fun

build(ctor: (P, UElement?) -> UExpression): () -> UExpression? { + return fun(): UExpression? { + val parent = if (parentCallback == null) null else (parentCallback() ?: return null) + return ctor(el as P, parent) + } + } + + return with (requiredType) { when (el) { + is PsiDeclarationStatement -> expr { + val parent = if (parentCallback == null) null else (parentCallback() ?: return null) + convertDeclarations(el.declaredElements, parent!!) + } + is PsiExpressionListStatement -> expr { + val parent = if (parentCallback == null) null else (parentCallback() ?: return null) + convertDeclarations(el.expressionList.expressions, parent!!) + } + is PsiBlockStatement -> expr(build(::JavaUBlockExpression)) + is PsiLabeledStatement -> expr(build(::JavaULabeledExpression)) + is PsiExpressionStatement -> convertExpression(el.expression, parentCallback, requiredType) + is PsiIfStatement -> expr(build(::JavaUIfExpression)) + is PsiSwitchStatement -> expr(build(::JavaUSwitchExpression)) + is PsiWhileStatement -> expr(build(::JavaUWhileExpression)) + is PsiDoWhileStatement -> expr(build(::JavaUDoWhileExpression)) + is PsiForStatement -> expr(build(::JavaUForExpression)) + is PsiForeachStatement -> expr(build(::JavaUForEachExpression)) + is PsiBreakStatement -> expr(build(::JavaUBreakExpression)) + is PsiContinueStatement -> expr(build(::JavaUContinueExpression)) + is PsiReturnStatement -> expr(build(::JavaUReturnExpression)) + is PsiAssertStatement -> expr(build(::JavaUAssertExpression)) + is PsiThrowStatement -> expr(build(::JavaUThrowExpression)) + is PsiSynchronizedStatement -> expr(build(::JavaUSynchronizedExpression)) + is PsiTryStatement -> expr(build(::JavaUTryExpression)) + is PsiEmptyStatement -> expr { UastEmptyExpression } + else -> expr(build(::UnknownJavaExpression)) + }} + } + + private fun convertDeclarations(elements: Array, parent: UElement): UDeclarationsExpression { + return JavaUDeclarationsExpression(parent).apply { + val declarations = mutableListOf() + for (element in elements) { + if (element is PsiVariable) { + declarations += JavaUVariable.create(element, this) + } + else if (element is PsiClass) { + declarations += JavaUClass.create(element, this) + } + } + this.declarations = declarations + } + } + + internal fun convertOrEmpty(statement: PsiStatement?, parent: UElement?): UExpression { + return statement?.let { convertStatement(it, parent.toCallback(), null) } ?: UastEmptyExpression + } + + internal fun convertOrEmpty(expression: PsiExpression?, parent: UElement?): UExpression { + return expression?.let { convertExpression(it, parent.toCallback()) } ?: UastEmptyExpression + } + + internal fun convertOrNull(expression: PsiExpression?, parent: UElement?): UExpression? { + return if (expression != null) convertExpression(expression, parent.toCallback()) else null + } + + internal fun convertOrEmpty(block: PsiCodeBlock?, parent: UElement?): UExpression { + return if (block != null) convertBlock(block, parent) else UastEmptyExpression + } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUDoWhileExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUDoWhileExpression.kt new file mode 100644 index 000000000000..0edac6d1a4f2 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUDoWhileExpression.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiDoWhileStatement +import com.intellij.psi.impl.source.tree.ChildRole +import org.jetbrains.uast.UDoWhileExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier + +class JavaUDoWhileExpression( + override val psi: PsiDoWhileStatement, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UDoWhileExpression { + override val condition by lz { JavaConverter.convertOrEmpty(psi.condition, this) } + override val body by lz { JavaConverter.convertOrEmpty(psi.body, this) } + + override val doIdentifier: UIdentifier + get() = UIdentifier(psi.getChildByRole(ChildRole.DO_KEYWORD), this) + override val whileIdentifier: UIdentifier + get() = UIdentifier(psi.getChildByRole(ChildRole.WHILE_KEYWORD), this) +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUForEachExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUForEachExpression.kt new file mode 100644 index 000000000000..3e81c589ee7b --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUForEachExpression.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiForeachStatement +import com.intellij.psi.impl.source.tree.ChildRole +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UForEachExpression +import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.UParameter + +class JavaUForEachExpression( + override val psi: PsiForeachStatement, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UForEachExpression { + override val variable: UParameter + get() = JavaUParameter(psi.iterationParameter, this) + + override val iteratedValue by lz { JavaConverter.convertOrEmpty(psi.iteratedValue, this) } + override val body by lz { JavaConverter.convertOrEmpty(psi.body, this) } + + override val forIdentifier: UIdentifier + get() = UIdentifier(psi.getChildByRole(ChildRole.FOR_KEYWORD), this) +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUForExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUForExpression.kt new file mode 100644 index 000000000000..e057b8a2aa2d --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUForExpression.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiForStatement +import com.intellij.psi.impl.source.tree.ChildRole +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UForExpression +import org.jetbrains.uast.UIdentifier + +class JavaUForExpression( + override val psi: PsiForStatement, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UForExpression { + override val declaration by lz { psi.initialization?.let { JavaConverter.convertStatement(it, { this }) } } + override val condition by lz { psi.condition?.let { JavaConverter.convertExpression(it, { this }) } } + override val update by lz { psi.update?.let { JavaConverter.convertStatement(it, { this }) } } + override val body by lz { JavaConverter.convertOrEmpty(psi.body, this) } + + override val forIdentifier: UIdentifier + get() = UIdentifier(psi.getChildByRole(ChildRole.FOR_KEYWORD), this) +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUIfExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUIfExpression.kt new file mode 100644 index 000000000000..9cae21dcd6d5 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUIfExpression.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiIfStatement +import com.intellij.psi.impl.source.tree.ChildRole +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.UIfExpression + +class JavaUIfExpression( + override val psi: PsiIfStatement, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UIfExpression { + override val condition by lz { JavaConverter.convertOrEmpty(psi.condition, this) } + override val thenExpression by lz { JavaConverter.convertOrEmpty(psi.thenBranch, this) } + override val elseExpression by lz { JavaConverter.convertOrEmpty(psi.elseBranch, this) } + + override val isTernary: Boolean + get() = false + + override val ifIdentifier: UIdentifier + get() = UIdentifier(psi.getChildByRole(ChildRole.IF_KEYWORD), this) + + override val elseIdentifier: UIdentifier? + get() = psi.getChildByRole(ChildRole.ELSE_KEYWORD)?.let { UIdentifier(it, this) } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUSwitchExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUSwitchExpression.kt new file mode 100644 index 000000000000..a048e9e538b5 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUSwitchExpression.kt @@ -0,0 +1,116 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.* +import com.intellij.psi.impl.source.tree.ChildRole +import org.jetbrains.uast.* +import org.jetbrains.uast.java.expressions.JavaUExpressionList +import org.jetbrains.uast.java.kinds.JavaSpecialExpressionKinds + +class JavaUSwitchExpression( + override val psi: PsiSwitchStatement, + override val uastParent: UElement? +) : JavaAbstractUExpression(), USwitchExpression { + override val expression by lz { JavaConverter.convertOrEmpty(psi.expression, this) } + + override val body: UExpressionList by lz { + object : JavaUExpressionList(psi, JavaSpecialExpressionKinds.SWITCH, this) { + override fun asRenderString() = expressions.joinToString("\n") { + it.asRenderString().withMargin + } + }.apply { + expressions = this@JavaUSwitchExpression.psi.body?.convertToSwitchEntryList(this) ?: emptyList() + } + } + + + override val switchIdentifier: UIdentifier + get() = UIdentifier(psi.getChildByRole(ChildRole.SWITCH_KEYWORD), this) +} + +private fun PsiCodeBlock.convertToSwitchEntryList(containingElement: UExpression): List { + var currentLabels = listOf() + var currentBody = listOf() + val result = mutableListOf() + for (statement in statements) { + if (statement is PsiSwitchLabelStatement) { + if (currentBody.isEmpty()) { + currentLabels += statement + } + else if (currentLabels.isNotEmpty()) { + result += JavaUSwitchEntry(currentLabels, currentBody, containingElement) + currentLabels = listOf(statement) + currentBody = listOf() + } + } + else { + currentBody += statement + } + } + if (currentLabels.isNotEmpty()) { + result += JavaUSwitchEntry(currentLabels, currentBody, containingElement) + } + return result +} + +class JavaUSwitchEntry( + val labels: List, + val statements: List, + override val uastParent: UExpression +) : JavaAbstractUExpression(), USwitchClauseExpressionWithBody { + override val psi: PsiSwitchLabelStatement = labels.first() + + override val caseValues by lz { + labels.mapNotNull { + if (it.isDefaultCase) { + JavaUDefaultCaseExpression + } + else { + val value = it.caseValue + value?.let { JavaConverter.convertExpression(it, { this }) } + } + } + } + + override val body: UExpressionList by lz { + object : JavaUExpressionList(psi, JavaSpecialExpressionKinds.SWITCH_ENTRY, this) { + override fun asRenderString() = buildString { + appendln("{") + expressions.forEach { appendln(it.asRenderString().withMargin) } + appendln("}") + } + }.apply { + val statements = this@JavaUSwitchEntry.statements + expressions = statements.map { JavaConverter.convertOrEmpty(it, this) } + } + } +} + +object JavaUDefaultCaseExpression : UExpression { + override val uastParent: UElement? + get() = null + + override val psi: PsiElement? + get() = null + + override val annotations: List + get() = emptyList() + + override fun asLogString() = "UDefaultCaseExpression" + + override fun asRenderString() = "else" +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUTernaryIfExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUTernaryIfExpression.kt new file mode 100644 index 000000000000..0438c02686a8 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUTernaryIfExpression.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiConditionalExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.UIfExpression + +class JavaUTernaryIfExpression( + override val psi: PsiConditionalExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UIfExpression { + override val condition by lz { JavaConverter.convertOrEmpty(psi.condition, this) } + override val thenExpression by lz { JavaConverter.convertOrEmpty(psi.thenExpression, this) } + override val elseExpression by lz { JavaConverter.convertOrEmpty(psi.elseExpression, this) } + + override val isTernary: Boolean + get() = true + + override val ifIdentifier: UIdentifier + get() = UIdentifier(null, this) + + override val elseIdentifier: UIdentifier? + get() = UIdentifier(null, this) +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUTryExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUTryExpression.kt new file mode 100644 index 000000000000..13cb8b1461a6 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUTryExpression.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.* +import com.intellij.psi.impl.source.tree.ChildRole +import org.jetbrains.uast.* + +class JavaUTryExpression( + override val psi: PsiTryStatement, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UTryExpression { + override val tryClause by lz { JavaConverter.convertOrEmpty(psi.tryBlock, this) } + override val catchClauses by lz { psi.catchSections.map { JavaUCatchClause(it, this) } } + override val finallyClause by lz { psi.finallyBlock?.let { JavaConverter.convertBlock(it, this) } } + + override val resourceVariables by lz { + psi.resourceList + ?.filterIsInstance() + ?.map { JavaUVariable.create(it, this) } + ?: emptyList() + } + + override val hasResources: Boolean + get() = psi.resourceList != null + + override val tryIdentifier: UIdentifier + get() = UIdentifier(psi.getChildByRole(ChildRole.TRY_KEYWORD), this) + + override val finallyIdentifier: UIdentifier? + get() = psi.getChildByRole(ChildRole.FINALLY_KEYWORD)?.let { UIdentifier(it, this) } +} + +class JavaUCatchClause( + override val psi: PsiCatchSection, + override val uastParent: UElement? +) : JavaAbstractUElement(), UCatchClause { + override val body by lz { JavaConverter.convertOrEmpty(psi.catchBlock, this) } + + override val parameters by lz { + (psi.parameter?.let { listOf(it) } ?: emptyList()).map { JavaUParameter(it, this) } + } + + override val typeReferences by lz { + val typeElement = psi.parameter?.typeElement ?: return@lz emptyList() + if (typeElement.type is PsiDisjunctionType) { + typeElement.children.filterIsInstance().map { JavaUTypeReferenceExpression(it, this) } + } else { + listOf(JavaUTypeReferenceExpression(typeElement, this)) + } + } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUWhileExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUWhileExpression.kt new file mode 100644 index 000000000000..79739d156285 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUWhileExpression.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiWhileStatement +import com.intellij.psi.impl.source.tree.ChildRole +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.UWhileExpression + +class JavaUWhileExpression( + override val psi: PsiWhileStatement, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UWhileExpression { + override val condition by lz { JavaConverter.convertOrEmpty(psi.condition, this) } + override val body by lz { JavaConverter.convertOrEmpty(psi.body, this) } + + override val whileIdentifier: UIdentifier + get() = UIdentifier(psi.getChildByRole(ChildRole.WHILE_KEYWORD), this) +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUAnnotation.kt b/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUAnnotation.kt new file mode 100644 index 000000000000..460a74d59473 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUAnnotation.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiAnnotation +import com.intellij.psi.PsiClass +import org.jetbrains.uast.* +import org.jetbrains.uast.java.expressions.JavaUNamedExpression + +class JavaUAnnotation( + override val psi: PsiAnnotation, + override val uastParent: UElement? +) : UAnnotation { + override val qualifiedName: String? + get() = psi.qualifiedName + + override val attributeValues: List by lz { + val context = getUastContext() + val attributes = psi.parameterList.attributes + + attributes.map { attribute -> JavaUNamedExpression(attribute, this) } + } + + override fun resolve(): PsiClass? = psi.nameReferenceElement?.resolve() as? PsiClass + + override fun findAttributeValue(name: String?): UExpression? { + val context = getUastContext() + val attributeValue = psi.findAttributeValue(name) ?: return null + return context.convertElement(attributeValue, this, null) as? UExpression ?: UastEmptyExpression + } + + override fun findDeclaredAttributeValue(name: String?): UExpression? { + val context = getUastContext() + val attributeValue = psi.findDeclaredAttributeValue(name) ?: return null + return context.convertElement(attributeValue, this, null) as? UExpression ?: UastEmptyExpression + } + + companion object { + @JvmStatic + fun wrap(annotation: PsiAnnotation): UAnnotation = JavaUAnnotation(annotation, null) + + @JvmStatic + fun wrap(annotations: List): List = annotations.map { JavaUAnnotation(it, null) } + + @JvmStatic + fun wrap(annotations: Array): List = annotations.map { JavaUAnnotation(it, null) } + } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUClass.kt b/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUClass.kt new file mode 100644 index 000000000000..8e29e2be383a --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUClass.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.java + +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiAnonymousClass +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiJavaCodeReferenceElement +import org.jetbrains.uast.* +import org.jetbrains.uast.java.internal.JavaUElementWithComments + +abstract class AbstractJavaUClass : UClass, JavaUElementWithComments { + override val uastDeclarations by lz { + mutableListOf().apply { + addAll(fields) + addAll(initializers) + addAll(methods) + addAll(innerClasses) + } + } + + override val uastSuperTypes: List + get() { + fun createJavaUTypeReferenceExpression(referenceElement: PsiJavaCodeReferenceElement) = + LazyJavaUTypeReferenceExpression(referenceElement, this) { + JavaPsiFacade.getElementFactory(referenceElement.project).createType(referenceElement) + } + + return psi.extendsList?.referenceElements?.map(::createJavaUTypeReferenceExpression).orEmpty() + + psi.implementsList?.referenceElements?.map(::createJavaUTypeReferenceExpression).orEmpty() + } + + override val uastAnchor: UElement? + get() = UIdentifier(psi.nameIdentifier, this) + + override val annotations: List + get() = psi.annotations.map { JavaUAnnotation(it, this) } + + override fun equals(other: Any?) = other is AbstractJavaUClass && psi == other.psi + override fun hashCode() = psi.hashCode() +} + +class JavaUClass private constructor(psi: PsiClass, override val uastParent: UElement?) : + AbstractJavaUClass(), PsiClass by psi { + override val psi = unwrap(psi) + + override fun getSuperClass(): UClass? = super.getSuperClass() + override fun getFields(): Array = super.getFields() + override fun getInitializers(): Array = super.getInitializers() + override fun getMethods(): Array = super.getMethods() + override fun getInnerClasses(): Array = super.getInnerClasses() + + companion object { + fun create(psi: PsiClass, containingElement: UElement?): UClass { + return if (psi is PsiAnonymousClass) + JavaUAnonymousClass(psi, containingElement) + else + JavaUClass(psi, containingElement) + } + } +} + +class JavaUAnonymousClass( + psi: PsiAnonymousClass, + override val uastParent: UElement? +) : AbstractJavaUClass(), UAnonymousClass, PsiAnonymousClass by psi { + override val psi: PsiAnonymousClass = unwrap(psi) + + override fun getSuperClass(): UClass? = super.getSuperClass() + override fun getFields(): Array = super.getFields() + override fun getInitializers(): Array = super.getInitializers() + override fun getMethods(): Array = super.getMethods() + override fun getInnerClasses(): Array = super.getInnerClasses() +} diff --git a/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUClassInitializer.kt b/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUClassInitializer.kt new file mode 100644 index 000000000000..ae554552453d --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUClassInitializer.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.java + +import com.intellij.psi.PsiClassInitializer +import org.jetbrains.uast.* +import org.jetbrains.uast.java.internal.JavaUElementWithComments + +class JavaUClassInitializer( + psi: PsiClassInitializer, + override val uastParent: UElement? +) : UClassInitializer, JavaUElementWithComments, PsiClassInitializer by psi { + override val psi = unwrap(psi) + + override val uastAnchor: UElement? + get() = null + + override val uastBody by lz { + getLanguagePlugin().convertElement(psi.body, this, null) as? UExpression ?: UastEmptyExpression + } + + override val annotations by lz { psi.annotations.map { JavaUAnnotation(it, this) } } + + override fun equals(other: Any?) = this === other + override fun hashCode() = psi.hashCode() +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUFile.kt b/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUFile.kt new file mode 100644 index 000000000000..502784629964 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUFile.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiComment +import com.intellij.psi.PsiJavaFile +import com.intellij.psi.PsiRecursiveElementWalkingVisitor +import org.jetbrains.uast.UAnnotation +import org.jetbrains.uast.UComment +import org.jetbrains.uast.UFile +import org.jetbrains.uast.UastLanguagePlugin +import java.util.* + +class JavaUFile(override val psi: PsiJavaFile, override val languagePlugin: UastLanguagePlugin) : UFile { + override val packageName: String + get() = psi.packageName + + override val imports by lz { + psi.importList?.allImportStatements?.map { JavaUImportStatement(it, this) } ?: listOf() + } + + override val annotations: List + get() = emptyList() + + override val classes by lz { psi.classes.map { JavaUClass.create(it, this) } } + + override val allCommentsInFile by lz { + val comments = ArrayList(0) + psi.accept(object : PsiRecursiveElementWalkingVisitor() { + override fun visitComment(comment: PsiComment) { + comments += UComment(comment, this@JavaUFile) + } + }) + comments + } + + override fun equals(other: Any?) = (other as? JavaUFile)?.psi == psi +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUImportStatement.kt b/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUImportStatement.kt new file mode 100644 index 000000000000..02392e6b76ce --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUImportStatement.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiImportStatementBase +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UImportStatement + +class JavaUImportStatement( + override val psi: PsiImportStatementBase, + override val uastParent: UElement? +) : UImportStatement { + override val isOnDemand: Boolean + get() = psi.isOnDemand + override val importReference by lz { psi.importReference?.let { JavaDumbUElement(it, this, it.qualifiedName) } } + override fun resolve() = psi.resolve() +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUMethod.kt b/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUMethod.kt new file mode 100644 index 000000000000..535e7021a3f2 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUMethod.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.java + +import com.intellij.psi.PsiAnnotationMethod +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiNameIdentifierOwner +import org.jetbrains.uast.* +import org.jetbrains.uast.java.internal.JavaUElementWithComments + +open class JavaUMethod( + psi: PsiMethod, + override val uastParent: UElement? +) : UMethod, JavaUElementWithComments, PsiMethod by psi { + override val psi = unwrap(psi) + + override val uastBody by lz { + val body = psi.body ?: return@lz null + getLanguagePlugin().convertElement(body, this) as? UExpression + } + + override val annotations by lz { psi.annotations.map { JavaUAnnotation(it, this) } } + + override val uastParameters by lz { + psi.parameterList.parameters.map { JavaUParameter(it, this) } + } + + override val isOverride: Boolean + get() = psi.modifierList.findAnnotation("java.lang.Override") != null + + override val uastAnchor: UElement + get() = UIdentifier((psi.originalElement as? PsiNameIdentifierOwner)?.nameIdentifier ?: psi.nameIdentifier, this) + + override fun equals(other: Any?) = other is JavaUMethod && psi == other.psi + override fun hashCode() = psi.hashCode() + + companion object { + fun create(psi: PsiMethod, languagePlugin: UastLanguagePlugin, containingElement: UElement?) = when (psi) { + is PsiAnnotationMethod -> JavaUAnnotationMethod(psi, languagePlugin, containingElement) + else -> JavaUMethod(psi, containingElement) + } + } +} + +class JavaUAnnotationMethod( + override val psi: PsiAnnotationMethod, + languagePlugin: UastLanguagePlugin, + containingElement: UElement? +) : JavaUMethod(psi, containingElement), UAnnotationMethod { + override val uastDefaultValue by lz { + val defaultValue = psi.defaultValue ?: return@lz null + languagePlugin.convertElement(defaultValue, this, null) as? UExpression + } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUVariable.kt b/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUVariable.kt new file mode 100644 index 000000000000..ce6b19ffbcd0 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUVariable.kt @@ -0,0 +1,128 @@ + /* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.java + +import com.intellij.psi.* +import org.jetbrains.uast.* +import org.jetbrains.uast.java.internal.JavaUElementWithComments + +abstract class AbstractJavaUVariable : PsiVariable, UVariable, JavaUElementWithComments { + override val uastInitializer by lz { + val initializer = psi.initializer ?: return@lz null + getLanguagePlugin().convertElement(initializer, this) as? UExpression + } + + override val annotations by lz { psi.annotations.map { JavaUAnnotation(it, this) } } + override val typeReference by lz { getLanguagePlugin().convertOpt(psi.typeElement, this) } + + override val uastAnchor: UElement + get() = UIdentifier(psi.nameIdentifier, this) + + override fun equals(other: Any?) = other is AbstractJavaUVariable && psi == other.psi + override fun hashCode() = psi.hashCode() +} + +open class JavaUVariable( + psi: PsiVariable, + override val uastParent: UElement? +) : AbstractJavaUVariable(), UVariable, PsiVariable by psi { + override val psi = unwrap(psi) + + companion object { + fun create(psi: PsiVariable, containingElement: UElement?): UVariable { + return when (psi) { + is PsiEnumConstant -> JavaUEnumConstant(psi, containingElement) + is PsiLocalVariable -> JavaULocalVariable(psi, containingElement) + is PsiParameter -> JavaUParameter(psi, containingElement) + is PsiField -> JavaUField(psi, containingElement) + else -> JavaUVariable(psi, containingElement) + } + } + } +} + +open class JavaUParameter( + psi: PsiParameter, + override val uastParent: UElement? +) : AbstractJavaUVariable(), UParameter, PsiParameter by psi { + override val psi = unwrap(psi) +} + +open class JavaUField( + psi: PsiField, + override val uastParent: UElement? +) : AbstractJavaUVariable(), UField, PsiField by psi { + override val psi = unwrap(psi) +} + +open class JavaULocalVariable( + psi: PsiLocalVariable, + override val uastParent: UElement? +) : AbstractJavaUVariable(), ULocalVariable, PsiLocalVariable by psi { + override val psi = unwrap(psi) +} + +open class JavaUEnumConstant( + psi: PsiEnumConstant, + override val uastParent: UElement? +) : AbstractJavaUVariable(), UEnumConstant, PsiEnumConstant by psi { + override val initializingClass: UClass? by lz { getLanguagePlugin().convertOpt(psi.initializingClass, this) } + + override val psi = unwrap(psi) + + override val kind: UastCallKind + get() = UastCallKind.CONSTRUCTOR_CALL + override val receiver: UExpression? + get() = null + override val receiverType: PsiType? + get() = null + override val methodIdentifier: UIdentifier? + get() = null + override val classReference: UReferenceExpression? + get() = JavaEnumConstantClassReference(psi, uastParent) + override val typeArgumentCount: Int + get() = 0 + override val typeArguments: List + get() = emptyList() + override val valueArgumentCount: Int + get() = psi.argumentList?.expressions?.size ?: 0 + + override val valueArguments by lz { + psi.argumentList?.expressions?.map { + getLanguagePlugin().convertElement(it, this) as? UExpression ?: UastEmptyExpression + } ?: emptyList() + } + + override val returnType: PsiType? + get() = psi.type + + override fun resolve() = psi.resolveMethod() + + override val methodName: String? + get() = null + + private class JavaEnumConstantClassReference( + override val psi: PsiEnumConstant, + override val uastParent: UElement? + ) : JavaAbstractUExpression(), USimpleNameReferenceExpression { + override fun resolve() = psi.containingClass + override val resolvedName: String? + get() = psi.containingClass?.name + override val identifier: String + get() = psi.containingClass?.name ?: "" + } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaDumbUElement.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaDumbUElement.kt new file mode 100644 index 000000000000..660dae65a07f --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaDumbUElement.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiElement +import org.jetbrains.uast.UElement +import org.jetbrains.uast.internal.log + +class JavaDumbUElement( + override val psi: PsiElement?, + override val uastParent: UElement?, + private val customRenderString: String? = null +) : JavaAbstractUElement(), UElement { + override fun asLogString() = log() + override fun asRenderString() = customRenderString ?: "" +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUArrayAccessExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUArrayAccessExpression.kt new file mode 100644 index 000000000000..fc6cb42aec29 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUArrayAccessExpression.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiArrayAccessExpression +import org.jetbrains.uast.UArrayAccessExpression +import org.jetbrains.uast.UElement + +class JavaUArrayAccessExpression( + override val psi: PsiArrayAccessExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UArrayAccessExpression { + override val receiver by lz { JavaConverter.convertOrEmpty(psi.arrayExpression, this) } + override val indices by lz { singletonListOrEmpty(JavaConverter.convertOrNull(psi.indexExpression, this)) } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUAssertExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUAssertExpression.kt new file mode 100644 index 000000000000..8132ed64ca6a --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUAssertExpression.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.java + +import com.intellij.psi.PsiAssertStatement +import com.intellij.psi.PsiType +import org.jetbrains.uast.* + + +class JavaUAssertExpression( + override val psi: PsiAssertStatement, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UCallExpression { + val condition: UExpression by lz { JavaConverter.convertOrEmpty(psi.assertCondition, this) } + val message: UExpression? by lz { JavaConverter.convertOrNull(psi.assertDescription, this) } + + override val methodIdentifier: UIdentifier? + get() = null + + override val classReference: UReferenceExpression? + get() = null + + override val methodName: String + get() = "assert" + + override val receiver: UExpression? + get() = null + + override val receiverType: PsiType? + get() = null + + override val valueArgumentCount: Int + get() = if (message != null) 2 else 1 + + override val valueArguments by lz { + val message = this.message + if (message != null) listOf(condition, message) else listOf(condition) + } + + override val typeArgumentCount: Int + get() = 0 + + override val typeArguments: List + get() = emptyList() + + override val returnType: PsiType + get() = PsiType.VOID + + override val kind: UastCallKind + get() = JavaUastCallKinds.ASSERT + + override fun resolve() = null +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUAssignmentExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUAssignmentExpression.kt new file mode 100644 index 000000000000..855930d158ed --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUAssignmentExpression.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiAssignmentExpression +import org.jetbrains.uast.UBinaryExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier + +class JavaUAssignmentExpression( + override val psi: PsiAssignmentExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UBinaryExpression { + override val leftOperand by lz { JavaConverter.convertOrEmpty(psi.lExpression, this) } + override val rightOperand by lz { JavaConverter.convertOrEmpty(psi.rExpression, this) } + override val operator by lz { psi.operationTokenType.getOperatorType() } + + override fun resolveOperator() = null + + override val operatorIdentifier: UIdentifier + get() = UIdentifier(psi.operationSign, this) +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUBinaryExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUBinaryExpression.kt new file mode 100644 index 000000000000..8a88a2952079 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUBinaryExpression.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiBinaryExpression +import org.jetbrains.uast.UBinaryExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier + +class JavaUBinaryExpression( + override val psi: PsiBinaryExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UBinaryExpression { + override val leftOperand by lz { JavaConverter.convertOrEmpty(psi.lOperand, this) } + override val rightOperand by lz { JavaConverter.convertOrEmpty(psi.rOperand, this) } + override val operator by lz { psi.operationTokenType.getOperatorType() } + + override val operatorIdentifier: UIdentifier + get() = UIdentifier(psi.operationSign, this) + + override fun resolveOperator() = null +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUBlockExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUBlockExpression.kt new file mode 100644 index 000000000000..7134554314a2 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUBlockExpression.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiBlockStatement +import org.jetbrains.uast.UBlockExpression +import org.jetbrains.uast.UElement + +class JavaUBlockExpression( + override val psi: PsiBlockStatement, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UBlockExpression { + override val expressions by lz { psi.codeBlock.statements.map { JavaConverter.convertOrEmpty(it, this) } } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUBreakExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUBreakExpression.kt new file mode 100644 index 000000000000..4c1353f4c5df --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUBreakExpression.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.java + +import com.intellij.psi.PsiBreakStatement +import org.jetbrains.uast.UBreakExpression +import org.jetbrains.uast.UElement + +class JavaUBreakExpression( + override val psi: PsiBreakStatement, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UBreakExpression { + override val label: String? + get() = psi.labelIdentifier?.text +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUCallableReferenceExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUCallableReferenceExpression.kt new file mode 100644 index 000000000000..2d77e6bb42e7 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUCallableReferenceExpression.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethodReferenceExpression +import com.intellij.psi.PsiNamedElement +import com.intellij.psi.PsiType +import org.jetbrains.uast.UCallableReferenceExpression +import org.jetbrains.uast.UElement + +class JavaUCallableReferenceExpression( + override val psi: PsiMethodReferenceExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UCallableReferenceExpression { + override val qualifierExpression by lz { JavaConverter.convertOrNull(psi.qualifierExpression, this) } + + override val qualifierType: PsiType? + get() = psi.qualifierType?.type + + override val callableName: String + get() = psi.referenceName.orAnonymous() + + override fun resolve() = psi.resolve() + + override val resolvedName: String? = (psi.resolve() as? PsiNamedElement)?.name +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUClassLiteralExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUClassLiteralExpression.kt new file mode 100644 index 000000000000..e1451dabb44c --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUClassLiteralExpression.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiClassObjectAccessExpression +import com.intellij.psi.PsiType +import org.jetbrains.uast.UClassLiteralExpression +import org.jetbrains.uast.UElement + +class JavaUClassLiteralExpression( + override val psi: PsiClassObjectAccessExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UClassLiteralExpression { + override val type: PsiType + get() = psi.operand.type + + override val expression by lz { JavaUTypeReferenceExpression(psi.operand, this) } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUCodeBlockExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUCodeBlockExpression.kt new file mode 100644 index 000000000000..ac82f78b5d76 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUCodeBlockExpression.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiCodeBlock +import org.jetbrains.uast.UBlockExpression +import org.jetbrains.uast.UElement + +class JavaUCodeBlockExpression( + override val psi: PsiCodeBlock, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UBlockExpression { + override val expressions by lz { psi.statements.map { JavaConverter.convertOrEmpty(it, this) } } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUCompositeQualifiedExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUCompositeQualifiedExpression.kt new file mode 100644 index 000000000000..b9c409de9a32 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUCompositeQualifiedExpression.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiNamedElement +import org.jetbrains.uast.* + +class JavaUCompositeQualifiedExpression( + override val psi: PsiElement, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UQualifiedReferenceExpression { + override lateinit var receiver: UExpression + internal set + + override lateinit var selector: UExpression + internal set + + override val resolvedName: String? + get() = (resolve() as? PsiNamedElement)?.name + + override fun resolve() = (selector as? UResolvable)?.resolve() + + override val accessType: UastQualifiedExpressionAccessType + get() = UastQualifiedExpressionAccessType.SIMPLE +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUContinueExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUContinueExpression.kt new file mode 100644 index 000000000000..f9336b9a949e --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUContinueExpression.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.java + +import com.intellij.psi.PsiContinueStatement +import org.jetbrains.uast.UContinueExpression +import org.jetbrains.uast.UElement + +class JavaUContinueExpression( + override val psi: PsiContinueStatement, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UContinueExpression { + override val label: String? + get() = psi.labelIdentifier?.text +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUDeclarationsExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUDeclarationsExpression.kt new file mode 100644 index 000000000000..ced16f893b90 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUDeclarationsExpression.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiElement +import org.jetbrains.uast.UAnnotation +import org.jetbrains.uast.UDeclaration +import org.jetbrains.uast.UDeclarationsExpression +import org.jetbrains.uast.UElement + +class JavaUDeclarationsExpression( + override val uastParent: UElement? +) : UDeclarationsExpression { + override lateinit var declarations: List + internal set + + constructor(parent: UElement?, declarations: List) : this(parent) { + this.declarations = declarations + } + + override val annotations: List + get() = emptyList() + + override val psi: PsiElement? + get() = null +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUExpressionList.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUExpressionList.kt new file mode 100644 index 000000000000..ccbe187409e0 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUExpressionList.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java.expressions + +import com.intellij.psi.PsiElement +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UExpression +import org.jetbrains.uast.UExpressionList +import org.jetbrains.uast.UastSpecialExpressionKind +import org.jetbrains.uast.java.JavaAbstractUExpression + +open class JavaUExpressionList( + override val psi: PsiElement?, + override val kind: UastSpecialExpressionKind, // original element + override val uastParent: UElement? +) : JavaAbstractUExpression(), UExpressionList { + override lateinit var expressions: List + internal set +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUInstanceCheckExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUInstanceCheckExpression.kt new file mode 100644 index 000000000000..53c756b9353c --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUInstanceCheckExpression.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiInstanceOfExpression +import com.intellij.psi.PsiType +import org.jetbrains.uast.UBinaryExpressionWithType +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UastBinaryExpressionWithTypeKind +import org.jetbrains.uast.UastErrorType + +class JavaUInstanceCheckExpression( + override val psi: PsiInstanceOfExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UBinaryExpressionWithType { + override val operand by lz { JavaConverter.convertOrEmpty(psi.operand, this) } + override val typeReference by lz { psi.checkType?.let { JavaUTypeReferenceExpression(it, this) } } + + override val type: PsiType + get() = psi.checkType?.type ?: UastErrorType + + override val operationKind: UastBinaryExpressionWithTypeKind.InstanceCheck + get() = UastBinaryExpressionWithTypeKind.INSTANCE_CHECK +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaULabeledExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaULabeledExpression.kt new file mode 100644 index 000000000000..f5464964791e --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaULabeledExpression.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiLabeledStatement +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.ULabeledExpression + +class JavaULabeledExpression( + override val psi: PsiLabeledStatement, + override val uastParent: UElement? +) : JavaAbstractUExpression(), ULabeledExpression { + override val label: String + get() = psi.labelIdentifier.text + + override val labelIdentifier: UIdentifier? + get() = UIdentifier(psi.labelIdentifier, this) + + override val expression by lz { JavaConverter.convertOrEmpty(psi.statement, this) } + + override fun evaluate() = expression.evaluate() +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaULambdaExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaULambdaExpression.kt new file mode 100644 index 000000000000..4f040cfecd61 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaULambdaExpression.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiCodeBlock +import com.intellij.psi.PsiExpression +import com.intellij.psi.PsiLambdaExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.ULambdaExpression +import org.jetbrains.uast.UastEmptyExpression + +class JavaULambdaExpression( + override val psi: PsiLambdaExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), ULambdaExpression { + override val valueParameters by lz { + psi.parameterList.parameters.map { JavaUParameter(it, this) } + } + + override val body by lz { + val b = psi.body + when (b) { + is PsiCodeBlock -> JavaConverter.convertBlock(b, this) + is PsiExpression -> JavaConverter.convertOrEmpty(b, this) + else -> UastEmptyExpression + } + } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaULiteralExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaULiteralExpression.kt new file mode 100644 index 000000000000..10ed37cc6636 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaULiteralExpression.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiLiteralExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.ULiteralExpression + +class JavaULiteralExpression( + override val psi: PsiLiteralExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), ULiteralExpression { + override fun evaluate() = psi.value + override val value by lz { evaluate() } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUNamedExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUNamedExpression.kt new file mode 100644 index 000000000000..2377f9cd1954 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUNamedExpression.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java.expressions + +import com.intellij.psi.PsiNameValuePair +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UExpression +import org.jetbrains.uast.UNamedExpression +import org.jetbrains.uast.UastEmptyExpression +import org.jetbrains.uast.java.JavaAbstractUExpression +import org.jetbrains.uast.java.JavaConverter +import org.jetbrains.uast.java.lz + +class JavaUNamedExpression( + override val psi: PsiNameValuePair, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UNamedExpression { + override fun evaluate(): Any? = expression.evaluate() + + override val name: String? + get() = psi.name + + override val expression: UExpression by lz { + psi.value?.let { value -> JavaConverter.convertPsiElement(value, { this }) } as? UExpression ?: UastEmptyExpression } +} diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUObjectLiteralExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUObjectLiteralExpression.kt new file mode 100644 index 000000000000..c4eaee44539f --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUObjectLiteralExpression.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiNewExpression +import com.intellij.psi.PsiType +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UObjectLiteralExpression +import org.jetbrains.uast.UReferenceExpression + +class JavaUObjectLiteralExpression( + override val psi: PsiNewExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UObjectLiteralExpression { + override val declaration by lz { JavaUClass.create(psi.anonymousClass!!, this) } + + override val classReference by lz { + psi.classReference?.let { ref -> + JavaConverter.convertReference(ref, { this }, null) as? UReferenceExpression + } + } + + override val valueArgumentCount: Int + get() = psi.argumentList?.expressions?.size ?: 0 + + override val valueArguments by lz { + psi.argumentList?.expressions?.map { JavaConverter.convertOrEmpty(it, this) } ?: emptyList() + } + + override val typeArgumentCount by lz { psi.classReference?.typeParameters?.size ?: 0 } + + override val typeArguments: List + get() = psi.classReference?.typeParameters?.toList() ?: emptyList() + + override fun resolve() = psi.resolveMethod() +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUParenthesizedExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUParenthesizedExpression.kt new file mode 100644 index 000000000000..ce157b723754 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUParenthesizedExpression.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiParenthesizedExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UParenthesizedExpression + +class JavaUParenthesizedExpression( + override val psi: PsiParenthesizedExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UParenthesizedExpression { + override val expression by lz { JavaConverter.convertOrEmpty(psi.expression, this) } + override fun evaluate() = expression.evaluate() +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUPolyadicExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUPolyadicExpression.kt new file mode 100644 index 000000000000..3c194395e781 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUPolyadicExpression.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiPolyadicExpression +import org.jetbrains.uast.* + + +class JavaUPolyadicExpression( + override val psi: PsiPolyadicExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UPolyadicExpression { + override val operands: List by lz { + psi.operands.map { JavaConverter.convertOrEmpty(it, this) } + } + + override val operator: UastBinaryOperator by lz { psi.operationTokenType.getOperatorType() } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUPostfixExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUPostfixExpression.kt new file mode 100644 index 000000000000..b2bb60df5385 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUPostfixExpression.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.JavaTokenType +import com.intellij.psi.PsiPostfixExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.UPostfixExpression +import org.jetbrains.uast.UastPostfixOperator + +class JavaUPostfixExpression( + override val psi: PsiPostfixExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UPostfixExpression { + override val operand by lz { JavaConverter.convertOrEmpty(psi.operand, this) } + + override val operatorIdentifier: UIdentifier? + get() = UIdentifier(psi.operationSign, this) + + override fun resolveOperator() = null + + override val operator = when (psi.operationTokenType) { + JavaTokenType.PLUSPLUS -> UastPostfixOperator.INC + JavaTokenType.MINUSMINUS -> UastPostfixOperator.DEC + else -> UastPostfixOperator.UNKNOWN + } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUPrefixExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUPrefixExpression.kt new file mode 100644 index 000000000000..113c5fe785d4 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUPrefixExpression.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.JavaTokenType +import com.intellij.psi.PsiPrefixExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.UPrefixExpression +import org.jetbrains.uast.UastPrefixOperator + +class JavaUPrefixExpression( + override val psi: PsiPrefixExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UPrefixExpression { + override val operand by lz { JavaConverter.convertOrEmpty(psi.operand, this) } + + override val operatorIdentifier: UIdentifier? + get() = UIdentifier(psi.operationSign, this) + + override fun resolveOperator() = null + + override val operator = when (psi.operationTokenType) { + JavaTokenType.PLUS -> UastPrefixOperator.UNARY_PLUS + JavaTokenType.MINUS -> UastPrefixOperator.UNARY_MINUS + JavaTokenType.PLUSPLUS -> UastPrefixOperator.INC + JavaTokenType.MINUSMINUS-> UastPrefixOperator.DEC + JavaTokenType.EXCL -> UastPrefixOperator.LOGICAL_NOT + JavaTokenType.TILDE -> UastPrefixOperator.BITWISE_NOT + else -> UastPrefixOperator.UNKNOWN + } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUQualifiedReferenceExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUQualifiedReferenceExpression.kt new file mode 100644 index 000000000000..bd4c288ceabc --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUQualifiedReferenceExpression.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiJavaCodeReferenceElement +import com.intellij.psi.PsiNamedElement +import org.jetbrains.uast.* + +class JavaUQualifiedReferenceExpression( + override val psi: PsiJavaCodeReferenceElement, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UQualifiedReferenceExpression { + override val receiver by lz { + psi.qualifier?.let { JavaConverter.convertPsiElement(it, { this }) as? UExpression } ?: UastEmptyExpression + } + + override val selector by lz { + JavaUSimpleNameReferenceExpression(psi.referenceNameElement, psi.referenceName ?: "", this, psi) + } + + override val accessType: UastQualifiedExpressionAccessType + get() = UastQualifiedExpressionAccessType.SIMPLE + + override val resolvedName: String? + get() = (psi.resolve() as? PsiNamedElement)?.name + + override fun resolve() = psi.resolve() +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUReturnExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUReturnExpression.kt new file mode 100644 index 000000000000..a2e89847fefd --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUReturnExpression.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.java + +import com.intellij.psi.PsiReturnStatement +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UReturnExpression + +class JavaUReturnExpression( + override val psi: PsiReturnStatement, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UReturnExpression { + override val returnExpression by lz { JavaConverter.convertOrNull(psi.returnValue, this) } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUSimpleNameReferenceExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUSimpleNameReferenceExpression.kt new file mode 100644 index 000000000000..0f5e5d674e6d --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUSimpleNameReferenceExpression.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.* +import org.jetbrains.uast.UElement +import org.jetbrains.uast.USimpleNameReferenceExpression +import org.jetbrains.uast.UTypeReferenceExpression + +class JavaUSimpleNameReferenceExpression( + override val psi: PsiElement?, + override val identifier: String, + override val uastParent: UElement?, + val reference: PsiReference? = null +) : JavaAbstractUExpression(), USimpleNameReferenceExpression { + override fun resolve() = (reference ?: psi as? PsiReference)?.resolve() + override val resolvedName: String? + get() = ((reference ?: psi as? PsiReference)?.resolve() as? PsiNamedElement)?.name +} + +class JavaUTypeReferenceExpression( + override val psi: PsiTypeElement, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UTypeReferenceExpression { + override val type: PsiType + get() = psi.type +} + +class LazyJavaUTypeReferenceExpression( + override val psi: PsiElement, + override val uastParent: UElement?, + private val typeSupplier: () -> PsiType +) : JavaAbstractUExpression(), UTypeReferenceExpression { + override val type: PsiType by lz { typeSupplier() } +} + +class JavaClassUSimpleNameReferenceExpression( + override val identifier: String, + val ref: PsiJavaReference, + override val psi: PsiElement?, + override val uastParent: UElement? +) : JavaAbstractUExpression(), USimpleNameReferenceExpression { + override fun resolve() = ref.resolve() + override val resolvedName: String? + get() = (ref.resolve() as? PsiNamedElement)?.name +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUSuperExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUSuperExpression.kt new file mode 100644 index 000000000000..e033adbe11f9 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUSuperExpression.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiSuperExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.USuperExpression + +class JavaUSuperExpression( + override val psi: PsiSuperExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), USuperExpression { + override val label: String? + get() = psi.qualifier?.qualifiedName + + override val labelIdentifier: UIdentifier? + get() = psi.qualifier?.let { UIdentifier(it, this) } + + override fun resolve() = psi.qualifier?.resolve() +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUSynchronizedExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUSynchronizedExpression.kt new file mode 100644 index 000000000000..9b2e110987f8 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUSynchronizedExpression.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.java.expressions + +import com.intellij.psi.PsiSynchronizedStatement +import org.jetbrains.uast.UBlockExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.java.JavaAbstractUExpression +import org.jetbrains.uast.java.JavaConverter +import org.jetbrains.uast.java.lz +import org.jetbrains.uast.visitor.UastVisitor + +class JavaUSynchronizedExpression( + override val psi: PsiSynchronizedStatement, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UBlockExpression { + override val expressions by lz { psi.body?.statements?.map { JavaConverter.convertOrEmpty(it, this) } ?: listOf() } + val lockExpression by lz { JavaConverter.convertOrEmpty(psi.lockExpression, this) } + + override fun accept(visitor: UastVisitor) { + if (visitor.visitBlockExpression(this)) return + expressions.acceptList(visitor) + lockExpression.accept(visitor) + visitor.afterVisitBlockExpression(this) + } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUThisExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUThisExpression.kt new file mode 100644 index 000000000000..97facc8ffaca --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUThisExpression.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiThisExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.UThisExpression + +class JavaUThisExpression( + override val psi: PsiThisExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UThisExpression { + override val label: String? + get() = psi.qualifier?.qualifiedName + + override val labelIdentifier: UIdentifier? + get() = psi.qualifier?.let { UIdentifier(it, this) } + + override fun resolve() = psi.qualifier?.resolve() +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUThrowExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUThrowExpression.kt new file mode 100644 index 000000000000..a78402d2a6a2 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUThrowExpression.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.java + +import com.intellij.psi.PsiThrowStatement +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UThrowExpression + +class JavaUThrowExpression( + override val psi: PsiThrowStatement, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UThrowExpression { + override val thrownExpression by lz { JavaConverter.convertOrEmpty(psi.exception, this) } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUTypeCastExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUTypeCastExpression.kt new file mode 100644 index 000000000000..750bcff5cdaf --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUTypeCastExpression.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiType +import com.intellij.psi.PsiTypeCastExpression +import org.jetbrains.uast.UBinaryExpressionWithType +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UastBinaryExpressionWithTypeKind +import org.jetbrains.uast.UastErrorType + +class JavaUTypeCastExpression( + override val psi: PsiTypeCastExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UBinaryExpressionWithType { + override val operand by lz { JavaConverter.convertOrEmpty(psi.operand, this) } + + override val type: PsiType + get() = psi.castType?.type ?: UastErrorType + + override val typeReference by lz { psi.castType?.let { JavaUTypeReferenceExpression(it, this) } } + + override val operationKind: UastBinaryExpressionWithTypeKind.TypeCast + get() = UastBinaryExpressionWithTypeKind.TYPE_CAST +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/UnknownJavaExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/UnknownJavaExpression.kt new file mode 100644 index 000000000000..19278398b01d --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/UnknownJavaExpression.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.PsiElement +import org.jetbrains.uast.UAnnotation +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UExpression + +class UnknownJavaExpression( + override val psi: PsiElement, + override val uastParent: UElement? +) : UExpression { + override fun asLogString() = "[!] " + UnknownJavaExpression::class.java.simpleName + " ($psi)" + + override val annotations: List + get() = emptyList() +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/javaUCallExpressions.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/javaUCallExpressions.kt new file mode 100644 index 000000000000..34f1f1d540e8 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/javaUCallExpressions.kt @@ -0,0 +1,225 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.psi.* +import com.intellij.psi.util.PsiTypesUtil +import org.jetbrains.uast.* +import org.jetbrains.uast.psi.UElementWithLocation + +class JavaUCallExpression( + override val psi: PsiMethodCallExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UCallExpression, UElementWithLocation { + override val kind: UastCallKind + get() = UastCallKind.METHOD_CALL + + override val methodIdentifier by lz { + val methodExpression = psi.methodExpression + val nameElement = methodExpression.referenceNameElement ?: return@lz null + UIdentifier(nameElement, this) + } + + override val classReference: UReferenceExpression? + get() = null + + override val valueArgumentCount by lz { psi.argumentList.expressions.size } + override val valueArguments by lz { psi.argumentList.expressions.map { JavaConverter.convertOrEmpty(it, this) } } + + override val typeArgumentCount by lz { psi.typeArguments.size } + + override val typeArguments: List + get() = psi.typeArguments.toList() + + override val returnType: PsiType? + get() = psi.type + + override val methodName: String? + get() = psi.methodExpression.referenceName + + override fun resolve() = psi.resolveMethod() + + override fun getStartOffset(): Int { + return psi.methodExpression.referenceNameElement?.textOffset ?: psi.methodExpression.textOffset + } + + override fun getEndOffset() = psi.textRange.endOffset + + override val receiver: UExpression? + get() { + return if (uastParent is UQualifiedReferenceExpression && uastParent.selector == this) + uastParent.receiver + else + null + } + + override val receiverType: PsiType? + get() { + val qualifierType = psi.methodExpression.qualifierExpression?.type + if (qualifierType != null) { + return qualifierType + } + + val method = resolve() ?: return null + if (method.hasModifierProperty(PsiModifier.STATIC)) return null + return method.containingClass?.let { PsiTypesUtil.getClassType(it) } + } +} + +class JavaConstructorUCallExpression( + override val psi: PsiNewExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UCallExpression { + override val kind by lz { + when { + psi.arrayInitializer != null -> UastCallKind.NEW_ARRAY_WITH_INITIALIZER + psi.arrayDimensions.isNotEmpty() -> UastCallKind.NEW_ARRAY_WITH_DIMENSIONS + else -> UastCallKind.CONSTRUCTOR_CALL + } + } + + override val receiver: UExpression? + get() = null + + override val receiverType: PsiType? + get() = null + + override val methodIdentifier: UIdentifier? + get() = null + + override val classReference by lz { + psi.classReference?.let { ref -> + JavaConverter.convertReference(ref, { this }, null) as? UReferenceExpression + } + } + + override val valueArgumentCount: Int + get() { + val initializer = psi.arrayInitializer + return if (initializer != null) { + initializer.initializers.size + } else if (psi.arrayDimensions.isNotEmpty()) { + psi.arrayDimensions.size + } else { + psi.argumentList?.expressions?.size ?: 0 + } + } + + override val valueArguments by lz { + val initializer = psi.arrayInitializer + if (initializer != null) { + initializer.initializers.map { JavaConverter.convertOrEmpty(it, this) } + } + else if (psi.arrayDimensions.isNotEmpty()) { + psi.arrayDimensions.map { JavaConverter.convertOrEmpty(it, this) } + } + else { + psi.argumentList?.expressions?.map { JavaConverter.convertOrEmpty(it, this) } ?: emptyList() + } + } + + override val typeArgumentCount by lz { psi.classReference?.typeParameters?.size ?: 0 } + + override val typeArguments: List + get() = psi.classReference?.typeParameters?.toList() ?: emptyList() + + override val returnType: PsiType? + get() = (psi.classReference?.resolve() as? PsiClass)?.let { PsiTypesUtil.getClassType(it) } ?: psi.type + + override val methodName: String? + get() = null + + override fun resolve() = psi.resolveMethod() +} + +class JavaArrayInitializerUCallExpression( + override val psi: PsiArrayInitializerExpression, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UCallExpression { + override val methodIdentifier: UIdentifier? + get() = null + + override val classReference: UReferenceExpression? + get() = null + + override val methodName: String? + get() = null + + override val valueArgumentCount by lz { psi.initializers.size } + override val valueArguments by lz { psi.initializers.map { JavaConverter.convertOrEmpty(it, this) } } + + override val typeArgumentCount: Int + get() = 0 + + override val typeArguments: List + get() = emptyList() + + override val returnType: PsiType? + get() = psi.type + + override val kind: UastCallKind + get() = UastCallKind.NESTED_ARRAY_INITIALIZER + + override fun resolve() = null + + override val receiver: UExpression? + get() = null + + override val receiverType: PsiType? + get() = null +} + +class JavaAnnotationArrayInitializerUCallExpression( + override val psi: PsiArrayInitializerMemberValue, + override val uastParent: UElement? +) : JavaAbstractUExpression(), UCallExpression { + override val kind: UastCallKind + get() = UastCallKind.NESTED_ARRAY_INITIALIZER + + override val methodIdentifier: UIdentifier? + get() = null + + override val classReference: UReferenceExpression? + get() = null + + override val methodName: String? + get() = null + + override val valueArgumentCount by lz { psi.initializers.size } + + override val valueArguments by lz { + psi.initializers.map { + JavaConverter.convertPsiElement(it, { this }) as? UExpression ?: UnknownJavaExpression(it, this) + } + } + + override val typeArgumentCount: Int + get() = 0 + + override val typeArguments: List + get() = emptyList() + + override val returnType: PsiType? + get() = null + + override fun resolve() = null + + override val receiver: UExpression? + get() = null + + override val receiverType: PsiType? + get() = null +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/internal/JavaUElementWithComments.kt b/uast/uast-java/src/org/jetbrains/uast/java/internal/JavaUElementWithComments.kt new file mode 100644 index 000000000000..4d0d663306d6 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/internal/JavaUElementWithComments.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java.internal + +import com.intellij.psi.PsiComment +import org.jetbrains.uast.UComment +import org.jetbrains.uast.UElement + +interface JavaUElementWithComments : UElement { + override val comments: List + get() { + val psi = psi ?: return emptyList() + return psi.children.filter { it is PsiComment }.map { UComment(it, this) } + } +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/internal/javaInternalUastUtils.kt b/uast/uast-java/src/org/jetbrains/uast/java/internal/javaInternalUastUtils.kt new file mode 100644 index 000000000000..2fa1779809c7 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/internal/javaInternalUastUtils.kt @@ -0,0 +1,85 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import com.intellij.openapi.util.Key +import com.intellij.psi.JavaTokenType +import com.intellij.psi.PsiAnnotation +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiModifierListOwner +import com.intellij.psi.impl.source.tree.CompositeElement +import com.intellij.psi.tree.IElementType +import org.jetbrains.uast.UDeclaration +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UastBinaryOperator +import java.lang.ref.WeakReference + +internal val JAVA_CACHED_UELEMENT_KEY = Key.create>("cached-java-uelement") + +internal fun IElementType.getOperatorType() = when (this) { + JavaTokenType.EQ -> UastBinaryOperator.ASSIGN + JavaTokenType.PLUS -> UastBinaryOperator.PLUS + JavaTokenType.MINUS -> UastBinaryOperator.MINUS + JavaTokenType.ASTERISK -> UastBinaryOperator.MULTIPLY + JavaTokenType.DIV -> UastBinaryOperator.DIV + JavaTokenType.PERC -> UastBinaryOperator.MOD + JavaTokenType.ANDAND -> UastBinaryOperator.LOGICAL_AND + JavaTokenType.OROR -> UastBinaryOperator.LOGICAL_OR + JavaTokenType.OR -> UastBinaryOperator.BITWISE_OR + JavaTokenType.AND -> UastBinaryOperator.BITWISE_AND + JavaTokenType.XOR -> UastBinaryOperator.BITWISE_XOR + JavaTokenType.EQEQ -> UastBinaryOperator.IDENTITY_EQUALS + JavaTokenType.NE -> UastBinaryOperator.IDENTITY_NOT_EQUALS + JavaTokenType.GT -> UastBinaryOperator.GREATER + JavaTokenType.GE -> UastBinaryOperator.GREATER_OR_EQUALS + JavaTokenType.LT -> UastBinaryOperator.LESS + JavaTokenType.LE -> UastBinaryOperator.LESS_OR_EQUALS + JavaTokenType.LTLT -> UastBinaryOperator.SHIFT_LEFT + JavaTokenType.GTGT -> UastBinaryOperator.SHIFT_RIGHT + JavaTokenType.GTGTGT -> UastBinaryOperator.UNSIGNED_SHIFT_RIGHT + JavaTokenType.PLUSEQ -> UastBinaryOperator.PLUS_ASSIGN + JavaTokenType.MINUSEQ -> UastBinaryOperator.MINUS_ASSIGN + JavaTokenType.ASTERISKEQ -> UastBinaryOperator.MULTIPLY_ASSIGN + JavaTokenType.DIVEQ -> UastBinaryOperator.DIVIDE_ASSIGN + JavaTokenType.PERCEQ -> UastBinaryOperator.REMAINDER_ASSIGN + JavaTokenType.ANDEQ -> UastBinaryOperator.AND_ASSIGN + JavaTokenType.XOREQ -> UastBinaryOperator.XOR_ASSIGN + JavaTokenType.OREQ -> UastBinaryOperator.OR_ASSIGN + JavaTokenType.LTLTEQ -> UastBinaryOperator.SHIFT_LEFT_ASSIGN + JavaTokenType.GTGTEQ -> UastBinaryOperator.SHIFT_RIGHT_ASSIGN + JavaTokenType.GTGTGTEQ -> UastBinaryOperator.UNSIGNED_SHIFT_RIGHT_ASSIGN + else -> UastBinaryOperator.OTHER +} + +internal fun singletonListOrEmpty(element: T?) = if (element != null) listOf(element) else emptyList() + +@Suppress("NOTHING_TO_INLINE") +internal inline fun String?.orAnonymous(kind: String = ""): String { + return this ?: "" +} + +internal fun lz(initializer: () -> T) = lazy(LazyThreadSafetyMode.NONE, initializer) + +val PsiModifierListOwner.annotations: Array + get() = modifierList?.annotations ?: emptyArray() + +internal inline fun unwrap(element: P): P { + val unwrapped = if (element is T) element.psi else element + assert(unwrapped !is UElement) + return unwrapped as P +} + +internal fun PsiElement.getChildByRole(role: Int) = (this as? CompositeElement)?.findChildByRoleAsPsiElement(role) \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/kinds/JavaSpecialExpressionKinds.kt b/uast/uast-java/src/org/jetbrains/uast/java/kinds/JavaSpecialExpressionKinds.kt new file mode 100644 index 000000000000..1fe99a24727e --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/kinds/JavaSpecialExpressionKinds.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java.kinds + +import org.jetbrains.uast.UastSpecialExpressionKind + +object JavaSpecialExpressionKinds { + @JvmField + val SWITCH = UastSpecialExpressionKind("switch") + + @JvmField + val SWITCH_ENTRY = UastSpecialExpressionKind("switch_entry") +} \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/kinds/JavaUastCallKinds.kt b/uast/uast-java/src/org/jetbrains/uast/java/kinds/JavaUastCallKinds.kt new file mode 100644 index 000000000000..d0d2bf346184 --- /dev/null +++ b/uast/uast-java/src/org/jetbrains/uast/java/kinds/JavaUastCallKinds.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.java + +import org.jetbrains.uast.UastCallKind + +object JavaUastCallKinds { + @JvmField + val ASSERT = UastCallKind("assert") +} \ No newline at end of file diff --git a/uast/uast-java/uast-java.iml b/uast/uast-java/uast-java.iml new file mode 100644 index 000000000000..064a249359fa --- /dev/null +++ b/uast/uast-java/uast-java.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/uast/uast-tests/src/org/jetbrains/uast/test/common/RenderLogTestBase.kt b/uast/uast-tests/src/org/jetbrains/uast/test/common/RenderLogTestBase.kt new file mode 100644 index 000000000000..ff298fa82b76 --- /dev/null +++ b/uast/uast-tests/src/org/jetbrains/uast/test/common/RenderLogTestBase.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.test.common + +import org.jetbrains.uast.UFile +import org.jetbrains.uast.asRecursiveLogString +import org.jetbrains.uast.test.env.assertEqualsToFile +import java.io.File + +interface RenderLogTestBase { + fun getTestFile(testName: String, ext: String): File + + private fun getRenderFile(testName: String) = getTestFile(testName, "render.txt") + private fun getLogFile(testName: String) = getTestFile(testName, "log.txt") + + fun check(testName: String, file: UFile) { + val renderFile = getRenderFile(testName) + val logFile = getLogFile(testName) + + assertEqualsToFile("Render string", renderFile, file.asRenderString()) + assertEqualsToFile("Log string", logFile, file.asRecursiveLogString()) + } +} \ No newline at end of file diff --git a/uast/uast-tests/src/org/jetbrains/uast/test/common/ResolveTestBase.kt b/uast/uast-tests/src/org/jetbrains/uast/test/common/ResolveTestBase.kt new file mode 100644 index 000000000000..4ac8b9a541fc --- /dev/null +++ b/uast/uast-tests/src/org/jetbrains/uast/test/common/ResolveTestBase.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.test.common + +import com.intellij.psi.PsiNamedElement +import org.jetbrains.uast.UFile +import org.jetbrains.uast.UResolvable +import org.jetbrains.uast.test.env.findElementByText +import org.junit.Assert.assertEquals + +interface ResolveTestBase { + fun check(testName: String, file: UFile) { + val refComment = file.allCommentsInFile.find { it.text.startsWith("// REF:") } ?: throw IllegalArgumentException("No // REF tag in file") + val resultComment = file.allCommentsInFile.find { it.text.startsWith("// RESULT:") } ?: throw IllegalArgumentException("No // RESULT tag in file") + + val refText = refComment.text.substringAfter("REF:") + val parent = refComment.uastParent + val matchingElement = parent.findElementByText(refText) + val resolveResult = matchingElement.resolve() ?: throw IllegalArgumentException("Unresolved reference") + val resultText = resolveResult.javaClass.simpleName + (if (resolveResult is PsiNamedElement) ":${resolveResult.name}" else "") + assertEquals(resultComment.text.substringAfter("RESULT:"), resultText) + } +} diff --git a/uast/uast-tests/src/org/jetbrains/uast/test/common/TypesTestBase.kt b/uast/uast-tests/src/org/jetbrains/uast/test/common/TypesTestBase.kt new file mode 100644 index 000000000000..316400de07a2 --- /dev/null +++ b/uast/uast-tests/src/org/jetbrains/uast/test/common/TypesTestBase.kt @@ -0,0 +1,71 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.test.common + +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UExpression +import org.jetbrains.uast.UFile +import org.jetbrains.uast.test.env.assertEqualsToFile +import org.jetbrains.uast.visitor.UastVisitor +import java.io.File + +interface TypesTestBase { + fun getTypesFile(testName: String): File + + private fun UFile.asLogTypes() = TypesLogger().apply { + this@asLogTypes.accept(this) + }.toString() + + fun check(testName: String, file: UFile) { + val valuesFile = getTypesFile(testName) + + assertEqualsToFile("Log values", valuesFile, file.asLogTypes()) + } + + class TypesLogger : UastVisitor { + + val builder = StringBuilder() + + var level = 0 + + override fun visitElement(node: UElement): Boolean { + val initialLine = node.asLogString() + " [" + run { + val renderString = node.asRenderString().lines() + if (renderString.size == 1) { + renderString.single() + } else { + renderString.first() + "..." + renderString.last() + } + } + "]" + + (1..level).forEach { builder.append(" ") } + builder.append(initialLine) + if (node is UExpression) { + val value = node.getExpressionType() + value?.let { builder.append(" : ").append(it) } + } + builder.appendln() + level++ + return false + } + + override fun afterVisitElement(node: UElement) { + level-- + } + + override fun toString() = builder.toString() + } +} \ No newline at end of file diff --git a/uast/uast-tests/src/org/jetbrains/uast/test/common/ValuesTestBase.kt b/uast/uast-tests/src/org/jetbrains/uast/test/common/ValuesTestBase.kt new file mode 100644 index 000000000000..9c9e198f8b63 --- /dev/null +++ b/uast/uast-tests/src/org/jetbrains/uast/test/common/ValuesTestBase.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.test.common + +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UExpression +import org.jetbrains.uast.UFile +import org.jetbrains.uast.evaluation.UEvaluationContext +import org.jetbrains.uast.evaluation.UEvaluatorExtension +import org.jetbrains.uast.evaluation.analyzeAll +import org.jetbrains.uast.test.env.assertEqualsToFile +import org.jetbrains.uast.visitor.UastVisitor +import java.io.File + +interface ValuesTestBase { + fun getValuesFile(testName: String): File + fun getEvaluatorExtension(): UEvaluatorExtension? = null + + private fun UFile.asLogValues(): String { + val evaluationContext = analyzeAll(extensions = getEvaluatorExtension()?.let { listOf(it) } ?: emptyList()) + return ValueLogger(evaluationContext).apply { + this@asLogValues.accept(this) + }.toString() + } + + fun check(testName: String, file: UFile) { + val valuesFile = getValuesFile(testName) + + assertEqualsToFile("Log values", valuesFile, file.asLogValues()) + } + + class ValueLogger(val evaluationContext: UEvaluationContext) : UastVisitor { + + val builder = StringBuilder() + + var level = 0 + + override fun visitElement(node: UElement): Boolean { + val initialLine = node.asLogString() + " [" + run { + val renderString = node.asRenderString().lines() + if (renderString.size == 1) { + renderString.single() + } else { + renderString.first() + "..." + renderString.last() + } + } + "]" + + (1..level).forEach { builder.append(" ") } + builder.append(initialLine) + if (node is UExpression) { + val value = evaluationContext.valueOf(node) + builder.append(" = ").append(value) + } + builder.appendln() + level++ + return false + } + + override fun afterVisitElement(node: UElement) { + level-- + } + + override fun toString() = builder.toString() + } +} \ No newline at end of file diff --git a/uast/uast-tests/src/org/jetbrains/uast/test/env/AbstractCoreEnvironment.kt b/uast/uast-tests/src/org/jetbrains/uast/test/env/AbstractCoreEnvironment.kt new file mode 100644 index 000000000000..a77433fcf830 --- /dev/null +++ b/uast/uast-tests/src/org/jetbrains/uast/test/env/AbstractCoreEnvironment.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.test.env + +import com.intellij.mock.MockProject +import java.io.File + +abstract class AbstractCoreEnvironment { + abstract val project: MockProject + + open fun dispose() { + // Do nothing + } + + abstract fun addJavaSourceRoot(root: File) + abstract fun addJar(root: File) +} \ No newline at end of file diff --git a/uast/uast-tests/src/org/jetbrains/uast/test/env/AbstractTestWithCoreEnvironment.kt b/uast/uast-tests/src/org/jetbrains/uast/test/env/AbstractTestWithCoreEnvironment.kt new file mode 100644 index 000000000000..f83dc2570d98 --- /dev/null +++ b/uast/uast-tests/src/org/jetbrains/uast/test/env/AbstractTestWithCoreEnvironment.kt @@ -0,0 +1,107 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.test.env + +import com.intellij.core.CoreApplicationEnvironment +import com.intellij.mock.MockProject +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.extensions.Extensions +import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.PsiManager +import com.intellij.rt.execution.junit.FileComparisonFailure +import junit.framework.TestCase +import org.jetbrains.uast.UastContext +import org.jetbrains.uast.UastLanguagePlugin +import org.jetbrains.uast.evaluation.UEvaluatorExtension +import org.jetbrains.uast.java.JavaUastLanguagePlugin +import java.io.File + +abstract class AbstractTestWithCoreEnvironment : TestCase() { + private var myEnvironment: AbstractCoreEnvironment? = null + + protected val environment: AbstractCoreEnvironment + get() = myEnvironment!! + + protected lateinit var project: MockProject + + protected val uastContext: UastContext by lazy { + ServiceManager.getService(project, UastContext::class.java) + } + + protected val psiManager: PsiManager by lazy { + PsiManager.getInstance(project) + } + + override fun tearDown() { + disposeEnvironment() + } + + protected abstract fun createEnvironment(source: File): AbstractCoreEnvironment + + protected fun initializeEnvironment(source: File) { + if (myEnvironment != null) { + error("Environment is already initialized") + } + myEnvironment = createEnvironment(source) + project = environment.project + + CoreApplicationEnvironment.registerExtensionPoint( + Extensions.getRootArea(), + UastLanguagePlugin.extensionPointName, + UastLanguagePlugin::class.java) + + CoreApplicationEnvironment.registerExtensionPoint( + Extensions.getRootArea(), + UEvaluatorExtension.EXTENSION_POINT_NAME, + UEvaluatorExtension::class.java) + + project.registerService(UastContext::class.java, UastContext::class.java) + + registerUastLanguagePlugins() + } + + private fun registerUastLanguagePlugins() { + val area = Extensions.getRootArea() + + area.getExtensionPoint(UastLanguagePlugin.extensionPointName) + .registerExtension(JavaUastLanguagePlugin()) + } + + protected fun disposeEnvironment() { + myEnvironment?.dispose() + myEnvironment = null + } +} + +private fun String.trimTrailingWhitespacesAndAddNewlineAtEOF(): String = + this.split('\n').map(String::trimEnd).joinToString(separator = "\n").let { + result -> if (result.endsWith("\n")) result else result + "\n" + } + +fun assertEqualsToFile(description: String, expected: File, actual: String) { + if (!expected.exists()) { + expected.writeText(actual) + TestCase.fail("File didn't exist. New file was created (${expected.canonicalPath}).") + } + + val expectedText = + StringUtil.convertLineSeparators(expected.readText().trim()).trimTrailingWhitespacesAndAddNewlineAtEOF() + val actualText = + StringUtil.convertLineSeparators(actual.trim()).trimTrailingWhitespacesAndAddNewlineAtEOF() + if (expectedText != actualText) { + throw FileComparisonFailure(description, expectedText, actualText, expected.absolutePath) + } +} diff --git a/uast/uast-tests/src/org/jetbrains/uast/test/env/AbstractUastTest.kt b/uast/uast-tests/src/org/jetbrains/uast/test/env/AbstractUastTest.kt new file mode 100644 index 000000000000..010b9b2305b3 --- /dev/null +++ b/uast/uast-tests/src/org/jetbrains/uast/test/env/AbstractUastTest.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.test.env + +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UFile +import org.jetbrains.uast.UastContext +import org.jetbrains.uast.visitor.UastVisitor +import java.io.File + +abstract class AbstractUastFixtureTest : LightCodeInsightFixtureTestCase() { + protected val uastContext: UastContext by lazy { + ServiceManager.getService(project, UastContext::class.java) + } + + abstract fun getVirtualFile(testName: String): VirtualFile + abstract fun check(testName: String, file: UFile) + + fun doTest(testName: String, checkCallback: (String, UFile) -> Unit = { testName, file -> check(testName, file) }) { + val virtualFile = getVirtualFile(testName) + + val psiFile = psiManager.findFile(virtualFile) ?: error("Can't get psi file for $testName") + val uFile = uastContext.convertElementWithParent(psiFile, null) ?: error("Can't get UFile for $testName") + checkCallback(testName, uFile as UFile) + } +} + +abstract class AbstractUastTest : AbstractTestWithCoreEnvironment() { + protected companion object { + val TEST_DATA_DIR = File("testData") + } + + abstract fun getVirtualFile(testName: String): VirtualFile + abstract fun check(testName: String, file: UFile) + + fun doTest(testName: String, checkCallback: (String, UFile) -> Unit = { testName, file -> check(testName, file) }) { + val virtualFile = getVirtualFile(testName) + + val psiFile = psiManager.findFile(virtualFile) ?: error("Can't get psi file for $testName") + val uFile = uastContext.convertElementWithParent(psiFile, null) ?: error("Can't get UFile for $testName") + checkCallback(testName, uFile as UFile) + } +} + +fun UElement.findElementByText(refText: String, cls: Class): T { + val matchingElements = mutableListOf() + accept(object : UastVisitor { + override fun visitElement(node: UElement): Boolean { + if (cls.isInstance(node) && node.psi?.text == refText) { + matchingElements.add(node as T) + } + return false + } + }) + + if (matchingElements.isEmpty()) { + throw IllegalArgumentException("Reference '$refText' not found") + } + if (matchingElements.size != 1) { + throw IllegalArgumentException("Reference '$refText' is ambiguous") + } + return matchingElements.single() +} + +inline fun UElement.findElementByText(refText: String): T = findElementByText(refText, T::class.java) diff --git a/uast/uast-tests/src/org/jetbrains/uast/test/env/TestCoreEnvironment.java b/uast/uast-tests/src/org/jetbrains/uast/test/env/TestCoreEnvironment.java new file mode 100644 index 000000000000..c4f0632f2e4b --- /dev/null +++ b/uast/uast-tests/src/org/jetbrains/uast/test/env/TestCoreEnvironment.java @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.test.env; + +import com.intellij.codeInsight.ContainerProvider; +import com.intellij.codeInsight.runner.JavaMainMethodProvider; +import com.intellij.core.CoreApplicationEnvironment; +import com.intellij.core.CoreJavaFileManager; +import com.intellij.core.JavaCoreApplicationEnvironment; +import com.intellij.core.JavaCoreProjectEnvironment; +import com.intellij.lang.MetaLanguage; +import com.intellij.mock.MockProject; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.extensions.ExtensionsArea; +import com.intellij.openapi.fileTypes.FileTypeExtensionPoint; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.vfs.StandardFileSystems; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileSystem; +import com.intellij.openapi.vfs.impl.ZipHandler; +import com.intellij.psi.FileContextProvider; +import com.intellij.psi.PsiElementFinder; +import com.intellij.psi.augment.PsiAugmentProvider; +import com.intellij.psi.compiled.ClassFileDecompilers; +import com.intellij.psi.impl.JavaClassSupersImpl; +import com.intellij.psi.impl.PsiElementFinderImpl; +import com.intellij.psi.impl.PsiTreeChangePreprocessor; +import com.intellij.psi.impl.compiled.ClsCustomNavigationPolicy; +import com.intellij.psi.impl.file.impl.JavaFileManager; +import com.intellij.psi.meta.MetaDataContributor; +import com.intellij.psi.stubs.BinaryFileStubBuilders; +import com.intellij.psi.util.JavaClassSupers; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; + +public class TestCoreEnvironment extends AbstractCoreEnvironment { + private static final Object APPLICATION_LOCK = new Object(); + private static volatile JavaCoreApplicationEnvironment sEnvironment = null; + + private final Disposable mDisposable; + private volatile JavaCoreProjectEnvironment mProjectEnvironment = null; + + public TestCoreEnvironment(Disposable disposable) { + mDisposable = disposable; + } + + @Override + public void dispose() { + Disposer.dispose(mDisposable); + } + + @Override + public MockProject getProject() { + JavaCoreProjectEnvironment projectEnvironment = getProjectEnvironment(); + if (projectEnvironment == null) { + return null; + } + return projectEnvironment.getProject(); + } + + @Override + public void addJavaSourceRoot(@NotNull File root) { + VirtualFileSystem vfs = StandardFileSystems.local(); + try { + addDirectoryToClassPath(vfs, root); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void addDirectoryToClassPath(VirtualFileSystem vfs, File root) throws IOException { + VirtualFile virtualFile = vfs.findFileByPath(root.getCanonicalPath()); + assert virtualFile != null; + getProjectEnvironment().addSourcesToClasspath(virtualFile); + } + + public void addJar(@NotNull File root) { + getProjectEnvironment().addJarToClassPath(root); + } + + public JavaCoreProjectEnvironment getProjectEnvironment() { + if (mProjectEnvironment != null) { + return mProjectEnvironment; + } + synchronized (APPLICATION_LOCK) { + if (mProjectEnvironment != null) { + return mProjectEnvironment; + } + JavaCoreApplicationEnvironment coreEnvironment = getCoreEnvironment(); + + mProjectEnvironment = new TestJavaCoreProjectEnvironment(coreEnvironment); + + Disposer.register(mDisposable, new Disposable() { + @Override + public void dispose() { + mProjectEnvironment = null; + } + }); + + return mProjectEnvironment; + } + } + + private static JavaCoreApplicationEnvironment getCoreEnvironment() { + if (sEnvironment != null) { + return sEnvironment; + } + synchronized (APPLICATION_LOCK) { + if (sEnvironment != null) { + return sEnvironment; + } + Disposable parentDisposable = Disposer.newDisposable(); + Extensions.cleanRootArea(parentDisposable); + registerAppExtensionPoints(); + JavaCoreApplicationEnvironment coreEnvironment = new JavaCoreApplicationEnvironment(parentDisposable); + coreEnvironment.registerApplicationService(JavaClassSupers.class, new JavaClassSupersImpl()); + + sEnvironment = coreEnvironment; + + Disposer.register(parentDisposable, new Disposable() { + @Override + public void dispose() { + synchronized (APPLICATION_LOCK) { + JavaCoreApplicationEnvironment environment = sEnvironment; + sEnvironment = null; + Disposer.dispose(environment.getParentDisposable()); + ZipHandler.clearFileAccessorCache(); + } + } + }); + return sEnvironment; + } + } + + private static void registerAppExtensionPoints() { + CoreApplicationEnvironment.registerExtensionPoint( + Extensions.getRootArea(), BinaryFileStubBuilders.EP_NAME, FileTypeExtensionPoint.class); + CoreApplicationEnvironment.registerExtensionPoint( + Extensions.getRootArea(), FileContextProvider.EP_NAME, FileContextProvider.class); + // + CoreApplicationEnvironment.registerExtensionPoint( + Extensions.getRootArea(), MetaDataContributor.EP_NAME, MetaDataContributor.class); + CoreApplicationEnvironment.registerExtensionPoint( + Extensions.getRootArea(), PsiAugmentProvider.EP_NAME, PsiAugmentProvider.class); + CoreApplicationEnvironment.registerExtensionPoint( + Extensions.getRootArea(), JavaMainMethodProvider.EP_NAME, JavaMainMethodProvider.class); + // + CoreApplicationEnvironment.registerExtensionPoint( + Extensions.getRootArea(), ContainerProvider.EP_NAME, ContainerProvider.class); + CoreApplicationEnvironment.registerExtensionPoint( + Extensions.getRootArea(), ClsCustomNavigationPolicy.EP_NAME, ClsCustomNavigationPolicy.class); + CoreApplicationEnvironment.registerExtensionPoint( + Extensions.getRootArea(), ClassFileDecompilers.EP_NAME, ClassFileDecompilers.Decompiler.class); + CoreApplicationEnvironment.registerExtensionPoint( + Extensions.getRootArea(), MetaLanguage.EP_NAME, MetaLanguage.class); + } + + private class TestJavaCoreProjectEnvironment extends JavaCoreProjectEnvironment { + TestJavaCoreProjectEnvironment(JavaCoreApplicationEnvironment coreEnvironment) { + super(TestCoreEnvironment.this.mDisposable, coreEnvironment); + registerProjectExtensions(); + } + + @Override + protected void preregisterServices() { + registerProjectExtensionPoints(); + } + + private void registerProjectExtensionPoints() { + ExtensionsArea area = Extensions.getArea(myProject); + CoreApplicationEnvironment.registerExtensionPoint( + area, PsiTreeChangePreprocessor.EP_NAME, PsiTreeChangePreprocessor.class); + CoreApplicationEnvironment.registerExtensionPoint( + area, PsiElementFinder.EP_NAME, PsiElementFinder.class); + } + + private void registerProjectExtensions() { + ExtensionsArea area = Extensions.getArea(myProject); + + myProject.registerService(CoreJavaFileManager.class, + ((CoreJavaFileManager) ServiceManager.getService(myProject, JavaFileManager.class))); + + area.getExtensionPoint(PsiElementFinder.EP_NAME).registerExtension( + new PsiElementFinderImpl(myProject, ServiceManager + .getService(myProject, JavaFileManager.class))); + } + } +} diff --git a/uast/uast-tests/test/org/jetbrains/uast/test/java/AbstractJavaRenderLogTest.kt b/uast/uast-tests/test/org/jetbrains/uast/test/java/AbstractJavaRenderLogTest.kt new file mode 100644 index 000000000000..7e05e0b19ae3 --- /dev/null +++ b/uast/uast-tests/test/org/jetbrains/uast/test/java/AbstractJavaRenderLogTest.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.test.java + +import org.jetbrains.uast.test.common.RenderLogTestBase +import java.io.File + +abstract class AbstractJavaRenderLogTest : AbstractJavaUastTest(), RenderLogTestBase { + override fun getTestFile(testName: String, ext: String) = + File(File(TEST_JAVA_MODEL_DIR, testName).canonicalPath.substringBeforeLast('.') + '.' + ext) +} \ No newline at end of file diff --git a/uast/uast-tests/test/org/jetbrains/uast/test/java/AbstractJavaUastTest.kt b/uast/uast-tests/test/org/jetbrains/uast/test/java/AbstractJavaUastTest.kt new file mode 100644 index 000000000000..03573072efa5 --- /dev/null +++ b/uast/uast-tests/test/org/jetbrains/uast/test/java/AbstractJavaUastTest.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.test.java + +import com.intellij.openapi.application.ex.PathManagerEx +import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vfs.VirtualFile +import org.jetbrains.uast.test.env.AbstractUastFixtureTest +import java.io.File + +abstract class AbstractJavaUastTest : AbstractUastFixtureTest() { + protected companion object { + val TEST_JAVA_MODEL_DIR = File(PathManagerEx.getCommunityHomePath(), "uast/uast-tests/java") + } + + override fun getVirtualFile(testName: String): VirtualFile { + return LocalFileSystem.getInstance().findFileByPath(File(TEST_JAVA_MODEL_DIR, testName).canonicalPath)!! + } +} \ No newline at end of file diff --git a/uast/uast-tests/test/org/jetbrains/uast/test/java/AbstractJavaValuesTest.kt b/uast/uast-tests/test/org/jetbrains/uast/test/java/AbstractJavaValuesTest.kt new file mode 100644 index 000000000000..03a830a2256f --- /dev/null +++ b/uast/uast-tests/test/org/jetbrains/uast/test/java/AbstractJavaValuesTest.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.test.java + +import org.jetbrains.uast.evaluation.UEvaluatorExtension +import org.jetbrains.uast.test.common.ValuesTestBase +import java.io.File + +abstract class AbstractJavaValuesTest : AbstractJavaUastTest(), ValuesTestBase { + protected var _evaluatorExtension: UEvaluatorExtension? = null + + override fun getEvaluatorExtension(): UEvaluatorExtension? = _evaluatorExtension + + private fun getTestFile(testName: String, ext: String) = + File(File(TEST_JAVA_MODEL_DIR, testName).canonicalPath.substringBeforeLast('.') + '.' + ext) + + override fun getValuesFile(testName: String) = getTestFile(testName, "values.txt") + + fun doTest(name: String, extension: UEvaluatorExtension) { + _evaluatorExtension = extension + try { + doTest(name) + } + finally { + _evaluatorExtension = null + } + } +} diff --git a/uast/uast-tests/test/org/jetbrains/uast/test/java/JavaUastApiTest.kt b/uast/uast-tests/test/org/jetbrains/uast/test/java/JavaUastApiTest.kt new file mode 100644 index 000000000000..0a04fff01dd0 --- /dev/null +++ b/uast/uast-tests/test/org/jetbrains/uast/test/java/JavaUastApiTest.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.test.java + +import org.jetbrains.uast.UFile +import org.jetbrains.uast.ULocalVariable +import org.jetbrains.uast.test.env.findElementByText +import org.junit.Test + +class JavaUastApiTest : AbstractJavaUastTest() { + override fun check(testName: String, file: UFile) { + } + + @Test fun testTypeReference() { + doTest("Simple/TypeReference.java") { name, file -> + val localVar = file.findElementByText("String s;") + val typeRef = localVar.typeReference + assertNotNull(typeRef) + } + } + + @Test fun testFields() { + doTest("Simple/Field.java") { name, file -> + assertEquals(1, file.classes[0].fields.size) + } + } +} diff --git a/uast/uast-tests/test/org/jetbrains/uast/test/java/JavaValuesTest.kt b/uast/uast-tests/test/org/jetbrains/uast/test/java/JavaValuesTest.kt new file mode 100644 index 000000000000..8305d3b301f7 --- /dev/null +++ b/uast/uast-tests/test/org/jetbrains/uast/test/java/JavaValuesTest.kt @@ -0,0 +1,149 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.test.java + +import com.intellij.psi.PsiMethod +import org.jetbrains.uast.UParameter +import org.jetbrains.uast.UVariable +import org.jetbrains.uast.evaluation.SimpleEvaluatorExtension +import org.jetbrains.uast.values.UBooleanConstant +import org.jetbrains.uast.values.UValue +import org.junit.Test + +class JavaValuesTest : AbstractJavaValuesTest() { + @Test fun testAliveThenElse() = doTest("Simple/AliveThenElse.java") + + @Test fun testAnonymous() = doTest("Simple/Anonymous.java") + + @Test fun testBitwise() = doTest("Simple/Bitwise.java") + + @Test fun testByteShort() = doTest("Simple/ByteShort.java") + + @Test fun testCascadeIf() = doTest("Simple/CascadeIf.java") + + @Test fun testCharacters() = doTest("Simple/Characters.java") + + @Test fun testClassLiteral() = doTest("Simple/ClassLiteral.java") + + @Test fun testDeadElse() = doTest("Simple/DeadElse.java") + + @Test fun testDeadFor() = doTest("Simple/DeadFor.java") + + @Test fun testDeadIfComparison() = doTest("Simple/DeadIfComparison.java") + + @Test fun testDeadSwitchEntries() = doTest("Simple/DeadSwitchEntries.java") + + @Test fun testDeadSwitchEntriesWithoutBreaks() = doTest("Simple/DeadSwitchEntriesWithoutBreaks.java") + + @Test fun testDeadThen() = doTest("Simple/DeadThen.java") + + @Test fun testDependents() = doTest("Simple/Dependents.java") + + @Test fun testDoWhile() = doTest("Simple/DoWhile.java") + + @Test fun testDoWhileInfinite() = doTest("Simple/DoWhileInfinite.java") + + @Test fun testDoWhileWithReturn() = doTest("Simple/DoWhileWithReturn.java") + + @Test fun testEnumChoice() = doTest("Simple/EnumChoice.java") + + @Test fun testEnumSwitch() = doTest("Simple/EnumSwitch.java") + + @Test fun testEnumSwitchConditionalBreak() = doTest("Simple/EnumSwitchConditionalBreak.java") + + @Test fun testEnumSwitchWithoutBreaks() = doTest("Simple/EnumSwitchWithoutBreaks.java") + + @Test fun testExternal() = doTest("Simple/External.java") + + @Test fun testFieldRef() = doTest("Simple/FieldRef.java") + + @Test fun testFloatDouble() = doTest("Simple/FloatDouble.java") + + @Test fun testFor() = doTest("Simple/For.java") + + @Test fun testForEach() = doTest("Simple/ForEach.java") + + @Test fun testForEachMutableIterable() = doTest("Simple/ForEachMutableIterable.java") + + @Test fun testIdentityEquals() = doTest("Simple/IdentityEquals.java") + + @Test fun testImmutableField() = doTest("Simple/ImmutableField.java") + + @Test fun testIncDec() = doTest("Simple/IncDec.java") + + @Test fun testIntLong() = doTest("Simple/IntLong.java") + + @Test fun testLabeled() = doTest("Simple/Labeled.java") + + @Test fun testLabeledOuter() = doTest("Simple/LabeledOuter.java") + + @Test fun testLambda() = doTest("Simple/Lambda.java") + + @Test fun testLogicals() = doTest("Simple/Logicals.java") + + @Test fun testMethodReference() = doTest("Simple/MethodReference.java") + + @Test fun testModification() = doTest("Simple/Modification.java") + + @Test fun testMutableField() = doTest("Simple/MutableField.java") + + @Test fun testNotANumber() = doTest("Simple/NotANumber.java") + + @Test fun testReturnMinusX() = doTest("Simple/ReturnMinusX.java") + + @Test fun testReturnSum() = doTest("Simple/ReturnSum.java") + + @Test fun testReturnX() = doTest("Simple/ReturnX.java") + + @Test fun testShift() = doTest("Simple/Shift.java") + + @Test fun testStrings() = doTest("Simple/Strings.java") + + @Test fun testTernary() = doTest("Simple/Ternary.java") + + @Test fun testTryCatch() = doTest("Simple/TryCatch.java") + + @Test fun testWhile() = doTest("Simple/While.java") + + @Test fun testWhileWithContinue() = doTest("Simple/WhileWithContinue.java") + + @Test fun testWhileWithIncrement() = doTest("Simple/WhileWithIncrement.java") + + @Test fun testWhileWithMutableCondition() = doTest("Simple/WhileWithMutableCondition.java") + + @Test fun testWhileWithReturn() = doTest("Simple/WhileWithReturn.java") + + @Test fun testEvaluatorExtension() = doTest("Simple/EvaluatorExtension.java", object : SimpleEvaluatorExtension() { + override fun evaluateMethodCall(target: PsiMethod, argumentValues: List): Any? { + if (target.name == "getTestName") { + (argumentValues.singleOrNull() as? UBooleanConstant)?.let { arg -> + return if (arg.value) "UPPER" else "lower" + } + } + + return super.evaluateMethodCall(target, argumentValues) + } + }) + + @Test fun testParamViaEvaluatorExtension() = doTest("Simple/ParamViaEvaluatorExtension.java", object : SimpleEvaluatorExtension() { + override fun evaluateVariable(variable: UVariable): Any? { + if (variable is UParameter && variable.name == "x") { + return "0" + } + return super.evaluateVariable(variable) + } + }) +} \ No newline at end of file diff --git a/uast/uast-tests/test/org/jetbrains/uast/test/java/SimpleJavaRenderLogTest.kt b/uast/uast-tests/test/org/jetbrains/uast/test/java/SimpleJavaRenderLogTest.kt new file mode 100644 index 000000000000..c7c08b98e6dd --- /dev/null +++ b/uast/uast-tests/test/org/jetbrains/uast/test/java/SimpleJavaRenderLogTest.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.uast.test.java + +import org.junit.Test + +class SimpleJavaRenderLogTest : AbstractJavaRenderLogTest() { + @Test fun testDataClass() = doTest("DataClass/DataClass.java") + + @Test fun testEnumSwitch() = doTest("Simple/EnumSwitch.java") + + @Test fun testLocalClass() = doTest("Simple/LocalClass.java") + + @Test fun testReturnX() = doTest("Simple/ReturnX.java") + + @Test fun testJava() = doTest("Simple/Simple.java") + + @Test fun testClass() = doTest("Simple/SuperTypes.java") + + @Test fun testTryWithResources() = doTest("Simple/TryWithResources.java") + + @Test fun testEnumValueMembers() = doTest("Simple/EnumValueMembers.java") + + @Test fun testQualifiedConstructorCall() = doTest("Simple/QualifiedConstructorCall.java") +} diff --git a/uast/uast-tests/uast-tests.iml b/uast/uast-tests/uast-tests.iml new file mode 100644 index 000000000000..0522488323b1 --- /dev/null +++ b/uast/uast-tests/uast-tests.iml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/uast/updateDependencies.gradle b/uast/updateDependencies.gradle new file mode 100644 index 000000000000..e477574f445f --- /dev/null +++ b/uast/updateDependencies.gradle @@ -0,0 +1,20 @@ +apply plugin: 'de.undercouch.download' + +task updateDependencies { + def downloadDir = new File(project.rootDir, "lib/download") + downloadDir.mkdirs() + + def intellijCoreZip = new File(downloadDir, "intellij-core.zip") + + download { + src "https://s3-eu-west-1.amazonaws.com/intellij-releases/com/jetbrains/intellij/idea/intellij-core/" + + "$intellij_core_version/intellij-core-${intellij_core_version}.zip" + dest intellijCoreZip + onlyIfNewer true + } + + copy { + from zipTree(intellijCoreZip) + into new File("lib/intellij-core") + } +} \ No newline at end of file diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DomManagerImpl.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DomManagerImpl.java index cf8cc12b2165..5f634a97f4bc 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/DomManagerImpl.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DomManagerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -116,7 +116,7 @@ public final class DomManagerImpl extends DomManager { } }, project); - VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() { + VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileListener() { private final List myDeletionEvents = new SmartList<>(); @Override diff --git a/xml/dom-openapi/src/com/intellij/util/xml/ui/UndoHelper.java b/xml/dom-openapi/src/com/intellij/util/xml/ui/UndoHelper.java index 97f5cc09d8ff..7a09653ad15b 100644 --- a/xml/dom-openapi/src/com/intellij/util/xml/ui/UndoHelper.java +++ b/xml/dom-openapi/src/com/intellij/util/xml/ui/UndoHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +15,8 @@ */ package com.intellij.util.xml.ui; -import com.intellij.openapi.command.CommandAdapter; import com.intellij.openapi.command.CommandEvent; +import com.intellij.openapi.command.CommandListener; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.event.DocumentAdapter; @@ -45,7 +45,7 @@ public class UndoHelper { public UndoHelper(final Project project, final Committable committable) { final PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project); - CommandProcessor.getInstance().addCommandListener(new CommandAdapter() { + CommandProcessor.getInstance().addCommandListener(new CommandListener() { @Override public void commandStarted(CommandEvent event) { undoTransparentActionStarted(); diff --git a/xml/impl/src/com/intellij/codeInsight/editorActions/XmlTagNameSynchronizer.java b/xml/impl/src/com/intellij/codeInsight/editorActions/XmlTagNameSynchronizer.java index f78f268cb2cf..076ced3fac06 100644 --- a/xml/impl/src/com/intellij/codeInsight/editorActions/XmlTagNameSynchronizer.java +++ b/xml/impl/src/com/intellij/codeInsight/editorActions/XmlTagNameSynchronizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,8 @@ import com.intellij.lang.xhtml.XHTMLLanguage; import com.intellij.lang.xml.XMLLanguage; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.command.CommandAdapter; import com.intellij.openapi.command.CommandEvent; +import com.intellij.openapi.command.CommandListener; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.undo.UndoManager; import com.intellij.openapi.components.NamedComponent; @@ -67,7 +67,7 @@ import java.util.Set; /** * @author Dennis.Ushakov */ -public class XmlTagNameSynchronizer extends CommandAdapter implements NamedComponent { +public class XmlTagNameSynchronizer implements NamedComponent, CommandListener { private static final Logger LOG = Logger.getInstance(XmlTagNameSynchronizer.class); private static final Set SUPPORTED_LANGUAGES = ContainerUtil.set(HTMLLanguage.INSTANCE, XMLLanguage.INSTANCE, diff --git a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java index b78b68a1f427..4cf33566d78e 100644 --- a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java +++ b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java @@ -30,7 +30,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupActivity; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileAdapter; +import com.intellij.openapi.vfs.VirtualFileListener; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.VirtualFilePropertyEvent; import com.intellij.openapi.vfs.impl.http.HttpVirtualFile; @@ -66,7 +66,7 @@ public class BreadcrumbsInitializingActivity implements StartupActivity, DumbAwa } } - private static class MyVirtualFileListener extends VirtualFileAdapter { + private static class MyVirtualFileListener implements VirtualFileListener { private final Project myProject; public MyVirtualFileListener(@NotNull Project project) { @@ -86,7 +86,7 @@ public class BreadcrumbsInitializingActivity implements StartupActivity, DumbAwa } private static void reinitBreadcrumbsInAllEditors(@NotNull Project project) { - if (!project.isDisposed()) return; + if (project.isDisposed()) return; FileEditorManager fileEditorManager = FileEditorManager.getInstance(project); for (VirtualFile virtualFile : fileEditorManager.getOpenFiles()) { reinitBreadcrumbsComponent(fileEditorManager, virtualFile); @@ -142,10 +142,10 @@ public class BreadcrumbsInitializingActivity implements StartupActivity, DumbAwa @NotNull BreadcrumbsXmlWrapper wrapper) { //noinspection deprecation if (wrapper.above) { - fileEditorManager.addTopComponent(fileEditor, wrapper.getComponent()); + fileEditorManager.addTopComponent(fileEditor, wrapper); } else { - fileEditorManager.addBottomComponent(fileEditor, wrapper.getComponent()); + fileEditorManager.addBottomComponent(fileEditor, wrapper); } Disposer.register(fileEditor, () -> disposeWrapper(fileEditorManager, fileEditor, wrapper)); } @@ -155,10 +155,10 @@ public class BreadcrumbsInitializingActivity implements StartupActivity, DumbAwa @NotNull BreadcrumbsXmlWrapper wrapper) { //noinspection deprecation if (wrapper.above) { - fileEditorManager.removeTopComponent(fileEditor, wrapper.getComponent()); + fileEditorManager.removeTopComponent(fileEditor, wrapper); } else { - fileEditorManager.removeBottomComponent(fileEditor, wrapper.getComponent()); + fileEditorManager.removeBottomComponent(fileEditor, wrapper); } Disposer.dispose(wrapper); } diff --git a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java index 976cca554fb9..8e5a45b8391c 100644 --- a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java +++ b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java @@ -22,6 +22,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.EditorGutter; import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.editor.colors.EditorColors; @@ -33,7 +34,6 @@ import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.editor.ex.EditorGutterComponentEx; import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; import com.intellij.openapi.editor.impl.ComplementaryFontsRegistry; -import com.intellij.openapi.editor.impl.EditorImpl; import com.intellij.openapi.editor.markup.RangeHighlighter; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.extensions.Extensions; @@ -71,10 +71,12 @@ import java.util.Collection; import java.util.LinkedList; import java.util.PriorityQueue; +import static com.intellij.ui.ScrollPaneFactory.createScrollPane; + /** * @author spleaner */ -public class BreadcrumbsXmlWrapper implements Disposable { +public class BreadcrumbsXmlWrapper extends JComponent implements Disposable { private static final class PsiCrumb extends Crumb.Impl { private final PsiElement element; private CrumbPresentation presentation; @@ -106,6 +108,24 @@ public class BreadcrumbsXmlWrapper implements Disposable { super.setFont(RelativeFont.SMALL.derive(font)); } + @Override + public Color getForeground() { + if (!isForegroundSet()) { + Color foreground = EditorColorsManager.getInstance().getGlobalScheme().getColor(EditorColors.LINE_NUMBERS_COLOR); + if (foreground != null) return foreground; + } + return super.getForeground(); + } + + @Override + public Color getBackground() { + if (!isBackgroundSet()) { + Color background = EditorColorsManager.getInstance().getGlobalScheme().getColor(EditorColors.GUTTER_BACKGROUND); + if (background != null) return background; + } + return super.getBackground(); + } + @Override protected Color getForeground(Crumb crumb) { CrumbPresentation presentation = PsiCrumb.getPresentation(crumb); @@ -131,7 +151,6 @@ public class BreadcrumbsXmlWrapper implements Disposable { private final static Logger LOG = Logger.getInstance(BreadcrumbsXmlWrapper.class); - private final JComponent myComponent = new JPanel(new BorderLayout()); private final Project myProject; private Editor myEditor; private Collection myHighlighed; @@ -225,25 +244,43 @@ public class BreadcrumbsXmlWrapper implements Disposable { breadcrumbs.onSelect(this::itemSelected); breadcrumbs.setFont(getEditorFont(myEditor)); - myComponent.setOpaque(false); - myComponent.add(BorderLayout.CENTER, breadcrumbs); + JScrollPane pane = createScrollPane(breadcrumbs, true); + pane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); + pane.getHorizontalScrollBar().setEnabled(false); + setLayout(new BorderLayout()); + add(BorderLayout.CENTER, pane); - final EditorGutterComponentEx gutterComponent = ((EditorImpl)editor).getGutterComponentEx(); - final ComponentAdapter resizeListener = new ComponentAdapter() { - @Override - public void componentResized(final ComponentEvent e) { - myComponent.setBorder(BorderFactory.createEmptyBorder(0, gutterComponent.getWhitespaceSeparatorOffset(), 0, 0)); - } - }; + EditorGutter gutter = editor.getGutter(); + if (gutter instanceof EditorGutterComponentEx) { + EditorGutterComponentEx gutterComponent = (EditorGutterComponentEx)gutter; + ComponentAdapter resizeListener = new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent event) { + updateBorder(gutterComponent.getWhitespaceSeparatorOffset()); + } + }; - gutterComponent.addComponentListener(resizeListener); - Disposer.register(this, () -> gutterComponent.removeComponentListener(resizeListener)); + addComponentListener(resizeListener); + gutterComponent.addComponentListener(resizeListener); + Disposer.register(this, () -> { + removeComponentListener(resizeListener); + gutterComponent.removeComponentListener(resizeListener); + }); + updateBorder(gutterComponent.getWhitespaceSeparatorOffset()); + } + else { + updateBorder(0); + } Disposer.register(this, new UiNotifyConnector(breadcrumbs, myQueue)); Disposer.register(this, myQueue); queueUpdate(); } + private void updateBorder(int offset) { + breadcrumbs.setBorder(BorderFactory.createEmptyBorder(above ? 2 : 0, offset, above ? 0 : 2, 0)); + } + private void updateCrumbs() { if (breadcrumbs != null && myEditor != null && !myEditor.isDisposed()) { breadcrumbs.setFont(getEditorFont(myEditor)); @@ -425,8 +462,9 @@ public class BreadcrumbsXmlWrapper implements Disposable { return null; } + @Deprecated public JComponent getComponent() { - return myComponent; + return this; } private void itemSelected(Crumb crumb, InputEvent event) { diff --git a/xml/xml-analysis-impl/src/com/intellij/codeInspection/htmlInspections/HtmlUnknownAttributeInspectionBase.java b/xml/xml-analysis-impl/src/com/intellij/codeInspection/htmlInspections/HtmlUnknownAttributeInspectionBase.java index c0aca36fdb8e..7895208865a7 100644 --- a/xml/xml-analysis-impl/src/com/intellij/codeInspection/htmlInspections/HtmlUnknownAttributeInspectionBase.java +++ b/xml/xml-analysis-impl/src/com/intellij/codeInspection/htmlInspections/HtmlUnknownAttributeInspectionBase.java @@ -16,13 +16,18 @@ package com.intellij.codeInspection.htmlInspections; import com.intellij.codeInsight.daemon.XmlErrorMessages; +import com.intellij.codeInsight.intention.HighPriorityAction; import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.psi.html.HtmlTag; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlTag; +import com.intellij.util.text.EditDistance; import com.intellij.xml.XmlAttributeDescriptor; import com.intellij.xml.XmlBundle; import com.intellij.xml.XmlElementDescriptor; @@ -33,6 +38,8 @@ import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; + public class HtmlUnknownAttributeInspectionBase extends HtmlUnknownElementInspection { private static final Key ATTRIBUTE_KEY = Key.create(ATTRIBUTE_SHORT_NAME); private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.htmlInspections.HtmlUnknownAttributeInspection"); @@ -44,7 +51,7 @@ public class HtmlUnknownAttributeInspectionBase extends HtmlUnknownElementInspec public HtmlUnknownAttributeInspectionBase(String defaultValues) { super(defaultValues); } - + @Override @Nls @NotNull @@ -92,17 +99,61 @@ public class HtmlUnknownAttributeInspectionBase extends HtmlUnknownElementInspec final String name = attribute.getName(); if (!XmlUtil.attributeFromTemplateFramework(name, tag) && (!isCustomValuesEnabled() || !isCustomValue(name))) { boolean maySwitchToHtml5 = HtmlUtil.isCustomHtml5Attribute(name) && !HtmlUtil.hasNonHtml5Doctype(tag); - LocalQuickFix[] quickfixes = new LocalQuickFix[maySwitchToHtml5 ? 3 : 2]; - quickfixes[0] = new AddCustomHtmlElementIntentionAction(ATTRIBUTE_KEY, name, XmlBundle.message("add.custom.html.attribute", name)); - quickfixes[1] = new RemoveAttributeIntentionAction(name); + ArrayList quickfixes = new ArrayList<>(6); + quickfixes + .add(new AddCustomHtmlElementIntentionAction(ATTRIBUTE_KEY, name, XmlBundle.message("add.custom.html.attribute", name))); + quickfixes.add(new RemoveAttributeIntentionAction(name)); if (maySwitchToHtml5) { - quickfixes[2] = new SwitchToHtml5WithHighPriorityAction(); + quickfixes.add(new SwitchToHtml5WithHighPriorityAction()); } + addSimilarAttributesQuickFixes(tag, name, quickfixes); registerProblemOnAttributeName(attribute, XmlErrorMessages.message("attribute.is.not.allowed.here", attribute.getName()), holder, - quickfixes); + quickfixes.toArray(LocalQuickFix.EMPTY_ARRAY)); } } } } + + private static void addSimilarAttributesQuickFixes(XmlTag tag, String name, ArrayList quickfixes) { + XmlElementDescriptor descriptor = tag.getDescriptor(); + if (descriptor == null) return; + XmlAttributeDescriptor[] descriptors = descriptor.getAttributesDescriptors(tag); + int initialSize = quickfixes.size(); + for (XmlAttributeDescriptor attr : descriptors) { + if (EditDistance.optimalAlignment(name, attr.getName(), false) <= 1) { + quickfixes.add(new RenameAttributeFix(attr)); + } + if (quickfixes.size() >= initialSize + 3) break; + } + } + + private static class RenameAttributeFix implements LocalQuickFix, HighPriorityAction { + private String name; + + public RenameAttributeFix(XmlAttributeDescriptor attr) { + name = attr.getName(); + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return "Rename attribute"; + } + + @Nls + @NotNull + @Override + public String getName() { + return "Rename attribute to " + name; + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + XmlAttribute attribute = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), XmlAttribute.class); + if (attribute == null) return; + attribute.setName(name); + } + } }