diff --git a/java/java-tests/java-tests.iml b/java/java-tests/java-tests.iml index b0496244a14f..9688f07f00fc 100644 --- a/java/java-tests/java-tests.iml +++ b/java/java-tests/java-tests.iml @@ -53,5 +53,6 @@ + \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java index e38307df86f9..7eef0b140706 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java @@ -30,6 +30,7 @@ import com.intellij.profile.codeInspection.InspectionProfileManager; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.profile.codeInspection.ui.header.InspectionToolsConfigurable; import com.intellij.testFramework.LightIdeaTestCase; +import com.intellij.util.JdomKt; import org.jdom.Element; import org.jdom.JDOMException; import org.jetbrains.annotations.NotNull; @@ -74,9 +75,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { profile.readExternal(element); final ModifiableModel model = profile.getModifiableModel(); model.commit(); - final Element copy = new Element("inspections"); - profile.writeExternal(copy); - assertElementsEqual(element, copy); + assertElementsEqual(element, profile.writeExternal()); } private static InspectionProfileImpl createProfile() { @@ -146,49 +145,47 @@ public class InspectionProfileTest extends LightIdeaTestCase { ModifiableModel model = profile.getModifiableModel(); model.commit(); - Element copy = new Element("inspections"); - profile.writeExternal(copy); - assertElementsEqual(loadProfile(), copy); + assertElementsEqual(loadProfile(), profile.writeExternal()); } private static Element loadProfile() throws IOException, JDOMException { - return JDOMUtil.loadDocument("\n" + - " ").getRootElement(); + return JdomKt.loadElement("\n" + + " "); } public void testReloadProfileWithUnknownScopes() throws Exception { - final Element element = JDOMUtil.loadDocument("\n" + + final Element element = JdomKt.loadElement("\n" + " ").getRootElement(); + ""); final InspectionProfileImpl profile = createProfile(); profile.readExternal(element); final ModifiableModel model = profile.getModifiableModel(); model.commit(); - final Element copy = new Element("inspections"); - profile.writeExternal(copy); - assertElementsEqual(element, copy); + assertElementsEqual(element, profile.writeExternal()); } public void testMergeUnusedDeclarationAndUnusedSymbol() throws Exception { //no specific settings - final Element element = JDOMUtil.loadDocument("\n" + + final Element element = JdomKt.loadElement("\n" + " ").getRootElement(); + ""); InspectionProfileImpl profile = createProfile(new InspectionProfileImpl("foo")); profile.readExternal(element); ModifiableModel model = profile.getModifiableModel(); model.commit(); - final Element copy = new Element("inspections"); - profile.writeExternal(copy); - assertElementsEqual(element, copy); + assertElementsEqual(element, profile.writeExternal()); //settings to merge - final Element unusedProfile = JDOMUtil.loadDocument("\n" + + final Element unusedProfile = JdomKt.loadElement("\n" + " ").getRootElement(); + ""); profile.readExternal(unusedProfile); model = profile.getModifiableModel(); model.commit(); @@ -292,8 +285,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { ""; assertEquals(mergedText, serialize(profile)); - Element toImportElement = new Element("profile"); - profile.writeExternal(toImportElement); + Element toImportElement = profile.writeExternal(); final InspectionProfileImpl importedProfile = InspectionToolsConfigurable.importInspectionProfile(toImportElement, InspectionProfileManager.getInstance(), getProject(), null); @@ -303,13 +295,9 @@ public class InspectionProfileTest extends LightIdeaTestCase { profile.readExternal(mergedElement); model = profile.getModifiableModel(); model.commit(); - Element copyMerged = new Element("profile"); - profile.writeExternal(copyMerged); - assertElementsEqual(mergedElement, copyMerged); + assertElementsEqual(mergedElement, profile.writeExternal()); - Element imported = new Element("profile"); - importedProfile.writeExternal(imported); - assertElementsEqual(mergedElement, imported); + assertElementsEqual(mergedElement, importedProfile.writeExternal()); } public void testDisabledUnusedDeclarationWithoutChanges() throws Exception { @@ -419,8 +407,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { "", serialize(profile)); - Element element = new Element("element"); - profile.writeExternal(element); + Element element = profile.writeExternal(); list.add(createTool("bar", true)); list.add(createTool("disabled", false)); @@ -447,9 +434,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { } private static String serialize(InspectionProfileImpl profile) throws WriteExternalException { - Element element = new Element("profile"); - profile.writeExternal(element); - return JDOMUtil.writeElement(element); + return JDOMUtil.writeElement(profile.writeExternal()); } private static InspectionProfileImpl createProfile(@NotNull InspectionToolRegistrar registrar) { @@ -508,7 +493,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { profile.enableTool(id, getProject()); } assertEquals(0, countInitializedTools(profile)); - profile.writeExternal(new Element("profile")); + profile.writeExternal(); List initializedTools = getInitializedTools(profile); if (initializedTools.size() > 0) { for (InspectionToolWrapper initializedTool : initializedTools) { @@ -543,9 +528,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { ""; foo.readExternal(JDOMUtil.loadDocument(test).getRootElement()); foo.initInspectionTools(getProject()); - Element serialized = new Element("profile"); - foo.writeExternal(serialized); - assertEquals(test, JDOMUtil.writeElement(serialized)); + assertEquals(test, JDOMUtil.writeElement(foo.writeExternal())); } public static int countInitializedTools(Profile foo) { diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionSchemeTest.kt b/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionSchemeTest.kt new file mode 100644 index 000000000000..7e97e48a8db2 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionSchemeTest.kt @@ -0,0 +1,64 @@ +/* + * 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. + */ +package com.intellij.codeInspection.ex + +import com.intellij.configurationStore.SchemeManagerFactoryBase +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.util.Computable +import com.intellij.testFramework.ProjectRule +import com.intellij.testFramework.TemporaryDirectory +import com.intellij.util.readText +import com.intellij.util.write +import org.assertj.core.api.Assertions.assertThat +import org.junit.ClassRule +import org.junit.Rule +import org.junit.Test + +internal class InspectionSchemeTest { + companion object { + @JvmField + @ClassRule val projectRule = ProjectRule() + } + + private val tempDirManager = TemporaryDirectory() + @Rule fun getTemporaryFolder() = tempDirManager + + @Test fun loadSchemes() { + val schemeDir = tempDirManager.newPath() + val schemeFile = schemeDir.resolve("inspection/Bar.xml") + val schemeData = """ + + """.trimIndent() + schemeFile.write(schemeData) + val schemeManagerFactory = SchemeManagerFactoryBase.TestSchemeManagerFactory(schemeDir) + val profileManager = InspectionProfileManagerImpl(InspectionToolRegistrar.getInstance(), schemeManagerFactory, ApplicationManager.getApplication().messageBus) + profileManager.forceInitProfiles(true) + profileManager.initProfiles() + + assertThat(profileManager.profiles).hasSize(1) + val scheme = profileManager.profiles.first() as InspectionProfileImpl + assertThat(scheme.name).isEqualTo("Bar") + + InspectionProfileImpl.initAndDo(Computable { scheme.initInspectionTools(null) }) + + schemeManagerFactory.save() + + assertThat(schemeFile.readText()).isEqualTo(schemeData) + profileManager.profiles + } +} diff --git a/platform/analysis-api/src/com/intellij/profile/DefaultProjectProfileManager.java b/platform/analysis-api/src/com/intellij/profile/DefaultProjectProfileManager.java index 275fea67cc81..692e7c852a35 100644 --- a/platform/analysis-api/src/com/intellij/profile/DefaultProjectProfileManager.java +++ b/platform/analysis-api/src/com/intellij/profile/DefaultProjectProfileManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * 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. @@ -60,7 +60,7 @@ public abstract class DefaultProjectProfileManager extends ProjectProfileManager private final ApplicationProfileManager myApplicationProfileManager; - private final Map myProfiles = new THashMap(); + private final Map myProfiles = new THashMap<>(); protected final DependencyValidationManager myHolder; private final List myProfilesListener = ContainerUtil.createLockFreeCopyOnWriteList(); @NonNls private static final String PROJECT_DEFAULT_PROFILE_NAME = "Project Default"; @@ -101,8 +101,7 @@ public abstract class DefaultProjectProfileManager extends ProjectProfileManager for (String profile : sortedProfiles) { final Profile projectProfile = myProfiles.get(profile); if (projectProfile != null) { - Element profileElement = new Element(PROFILE); - projectProfile.writeExternal(profileElement); + Element profileElement = projectProfile.writeExternal(); boolean hasSmthToSave = sortedProfiles.length > 1 || isCustomProfileUsed(); if (!hasSmthToSave) { for (Element child : profileElement.getChildren()) { diff --git a/platform/analysis-api/src/com/intellij/profile/Profile.java b/platform/analysis-api/src/com/intellij/profile/Profile.java index e9945762ebc0..0c83cb11bf78 100644 --- a/platform/analysis-api/src/com/intellij/profile/Profile.java +++ b/platform/analysis-api/src/com/intellij/profile/Profile.java @@ -51,5 +51,12 @@ public interface Profile extends Comparable, Scheme { void readExternal(Element element); - void writeExternal(Element element); + default void writeExternal(Element element) { + Element result = writeExternal(); + element.getAttributes().addAll(result.getAttributes()); + element.addContent(result.getContent()); + } + + @NotNull + Element writeExternal(); } diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java index de1c83fedfa7..246b73624b16 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java @@ -262,6 +262,17 @@ public class InspectionProfileImpl extends ProfileEx implements ModifiableModel, } } + @NotNull + @Override + public Element writeExternal() { + if (myDataHolder == null) { + return super.writeExternal(); + } + else { + return myDataHolder.read(); + } + } + @NotNull public Set getUsedSeverities() { LOG.assertTrue(myInitialized); @@ -565,7 +576,12 @@ public class InspectionProfileImpl extends ProfileEx implements ModifiableModel, for (InspectionToolWrapper toolWrapper : tools) { addTool(project, toolWrapper, dependencies); } - final GraphGenerator graphGenerator = GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph() { + + if (dataHolder != null) { + dataHolder.updateDigest(); + } + + DFSTBuilder builder = new DFSTBuilder<>(GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph() { @Override public Collection getNodes() { return dependencies.keySet(); @@ -575,9 +591,7 @@ public class InspectionProfileImpl extends ProfileEx implements ModifiableModel, public Iterator getIn(String n) { return dependencies.get(n).iterator(); } - })); - - DFSTBuilder builder = new DFSTBuilder<>(graphGenerator); + }))); if (builder.isAcyclic()) { final List scopes = builder.getSortedNodes(); myScopesOrder = ArrayUtil.toStringArray(scopes); diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionToolRegistrar.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionToolRegistrar.java index 324b720ef631..10e6918131c7 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionToolRegistrar.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionToolRegistrar.java @@ -61,11 +61,6 @@ public class InspectionToolRegistrar { if (!isInternal && ep.isInternal) continue; factories.add(() -> new GlobalInspectionToolWrapper(ep)); } - for (InspectionToolsFactory factory : Extensions.getExtensions(InspectionToolsFactory.EXTENSION_POINT_NAME)) { - for (final InspectionProfileEntry profileEntry : factory.createTools()) { - factories.add(() -> wrapTool(profileEntry)); - } - } myInspectionToolFactories.addAll(factories); } } diff --git a/platform/analysis-impl/src/com/intellij/profile/ProfileEx.java b/platform/analysis-impl/src/com/intellij/profile/ProfileEx.java index 388a3c8eac12..684989658088 100644 --- a/platform/analysis-impl/src/com/intellij/profile/ProfileEx.java +++ b/platform/analysis-impl/src/com/intellij/profile/ProfileEx.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * 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. @@ -22,6 +22,8 @@ import com.intellij.util.xmlb.annotations.Transient; import org.jdom.Element; import org.jetbrains.annotations.NotNull; +import static com.intellij.profile.DefaultProjectProfileManager.PROFILE; + /** * User: anna * Date: 01-Dec-2005 @@ -63,9 +65,7 @@ public abstract class ProfileEx implements Profile { @Override public void copyFrom(@NotNull Profile profile) { - Element config = new Element("config"); - profile.writeExternal(config); - readExternal(config); + readExternal(profile.writeExternal()); } @Override @@ -116,9 +116,11 @@ public abstract class ProfileEx implements Profile { mySerializer.writeExternal(this, element, preserveCompatibility); } - @Override - public void writeExternal(Element element) { - serializeInto(element, true); + @NotNull + public Element writeExternal() { + Element result = new Element(PROFILE); + serializeInto(result, true); + return result; } public void profileChanged() { diff --git a/platform/configuration-store-impl/src/BinaryXmlOutputter.kt b/platform/configuration-store-impl/src/BinaryXmlOutputter.kt index 71303360a4fb..bdcbe16be468 100644 --- a/platform/configuration-store-impl/src/BinaryXmlOutputter.kt +++ b/platform/configuration-store-impl/src/BinaryXmlOutputter.kt @@ -10,14 +10,10 @@ private enum class TypeMarker { ELEMENT, CDATA, TEXT, ELEMENT_END } -fun writeElement(element: Element, out: OutputStream) { +fun serializeElementToBinary(element: Element, out: OutputStream) { writeElement(element, DataOutputStream(out)) } -fun writeElement(element: Element, out: DataOutputStream) { - writeElement(out, element) -} - fun readElement(input: InputStream) = readElement(DataInputStream(input)) fun readElement(input: DataInputStream): Element { @@ -38,7 +34,7 @@ private fun readContent(element: Element, input: DataInputStream) { } } -private fun writeElement(out: DataOutputStream, element: Element) { +private fun writeElement(element: Element, out: DataOutputStream) { out.writeUTF(element.name) writeAttributes(out, element.attributes) @@ -47,7 +43,7 @@ private fun writeElement(out: DataOutputStream, element: Element) { for (item in content) { if (item is Element) { out.writeByte(TypeMarker.ELEMENT.ordinal) - writeElement(out, item) + writeElement(item, out) } else if (item is Text) { if (!isAllWhitespace(item)) { diff --git a/platform/configuration-store-impl/src/SchemeManagerFactoryImpl.kt b/platform/configuration-store-impl/src/SchemeManagerFactoryImpl.kt index 4fefe19c344e..9ce81c430ae9 100644 --- a/platform/configuration-store-impl/src/SchemeManagerFactoryImpl.kt +++ b/platform/configuration-store-impl/src/SchemeManagerFactoryImpl.kt @@ -20,7 +20,6 @@ import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.ComponentManager import com.intellij.openapi.components.RoamingType import com.intellij.openapi.components.SettingsSavingComponent -import com.intellij.openapi.components.impl.stores.StateStorageManager import com.intellij.openapi.components.stateStore import com.intellij.openapi.options.Scheme import com.intellij.openapi.options.SchemeManager @@ -30,6 +29,7 @@ import com.intellij.openapi.project.Project import com.intellij.util.SmartList import com.intellij.util.containers.ContainerUtil import com.intellij.util.lang.CompoundRuntimeException +import org.jetbrains.annotations.TestOnly import java.nio.file.Path import java.nio.file.Paths @@ -38,13 +38,11 @@ const val ROOT_CONFIG = "\$ROOT_CONFIG$" sealed class SchemeManagerFactoryBase : SchemeManagerFactory(), SettingsSavingComponent { private val managers = ContainerUtil.createLockFreeCopyOnWriteList>() - abstract val componentManager: ComponentManager + protected open val componentManager: ComponentManager? = null override final fun create(directoryName: String, processor: SchemeProcessor, presentableName: String?, roamingType: RoamingType): SchemeManager { - val storageManager = (componentManager.stateStore).stateStorageManager - val path = checkPath(directoryName) - val manager = SchemeManagerImpl(path, processor, (storageManager as? StateStorageManagerImpl)?.streamProvider, pathToFile(path, storageManager), roamingType, componentManager, presentableName) + val manager = SchemeManagerImpl(path, processor, (componentManager?.stateStore?.stateStorageManager as? StateStorageManagerImpl)?.streamProvider, pathToFile(path), roamingType, componentManager, presentableName) @Suppress("CAST_NEVER_SUCCEEDS") managers.add(manager as SchemeManagerImpl) return manager @@ -63,7 +61,7 @@ sealed class SchemeManagerFactoryBase : SchemeManagerFactory(), SettingsSavingCo return originalPath } - abstract fun pathToFile(path: String, storageManager: StateStorageManager): Path + abstract fun pathToFile(path: String): Path fun process(processor: (SchemeManagerImpl) -> Unit) { for (manager in managers) { @@ -105,13 +103,18 @@ sealed class SchemeManagerFactoryBase : SchemeManagerFactory(), SettingsSavingCo return path } - override fun pathToFile(path: String, storageManager: StateStorageManager) = Paths.get(storageManager.expandMacros(ROOT_CONFIG), path) + override fun pathToFile(path: String) = Paths.get(ApplicationManager.getApplication().stateStore.stateStorageManager.expandMacros(ROOT_CONFIG), path) } @Suppress("unused") private class ProjectSchemeManagerFactory(private val project: Project) : SchemeManagerFactoryBase() { override val componentManager = project - override fun pathToFile(path: String, storageManager: StateStorageManager) = Paths.get(project.basePath, if (ProjectUtil.isDirectoryBased(project)) "${Project.DIRECTORY_STORE_FOLDER}/$path" else ".$path") + override fun pathToFile(path: String) = Paths.get(project.basePath, if (ProjectUtil.isDirectoryBased(project)) "${Project.DIRECTORY_STORE_FOLDER}/$path" else ".$path") + } + + @TestOnly + class TestSchemeManagerFactory(private val basePath: Path) : SchemeManagerFactoryBase() { + override fun pathToFile(path: String) = basePath.resolve(path) } } \ No newline at end of file diff --git a/platform/configuration-store-impl/src/SchemeManagerImpl.kt b/platform/configuration-store-impl/src/SchemeManagerImpl.kt index 3bc4f7f21dd2..bb07afc00e99 100644 --- a/platform/configuration-store-impl/src/SchemeManagerImpl.kt +++ b/platform/configuration-store-impl/src/SchemeManagerImpl.kt @@ -30,6 +30,7 @@ import com.intellij.openapi.project.ProjectBundle import com.intellij.openapi.util.Comparing import com.intellij.openapi.util.Condition import com.intellij.openapi.util.JDOMUtil +import com.intellij.openapi.util.WriteExternalException import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.text.StringUtilRt import com.intellij.openapi.vfs.* @@ -48,7 +49,9 @@ import org.xmlpull.mxp1.MXParser import org.xmlpull.v1.XmlPullParser import java.io.IOException import java.io.InputStream +import java.io.OutputStream import java.nio.file.Path +import java.security.MessageDigest import java.util.* import java.util.function.Function @@ -216,7 +219,7 @@ class SchemeManagerImpl(val fileSpec: String, val fileName = PathUtilRt.getFileName(url.path) val extension = getFileExtension(fileName, true) val info = ExternalInfo(fileName.substring(0, fileName.length - extension.length), extension) - info.hash = element.getTreeHash() + info.digest = element.digest() info.schemeName = scheme.name val oldInfo = schemeToInfo.put(scheme, info) LOG.assertTrue(oldInfo == null) @@ -338,13 +341,24 @@ class SchemeManagerImpl(val fileSpec: String, return info != null && schemeExtension != info.fileExtension } - private class SchemeDataHolderImpl(private val bytes: ByteArray, private val externalInfo: ExternalInfo) : SchemeDataHolder { - override fun read(): Element { - val element = loadElement(bytes.inputStream()) - if (externalInfo.hash == 0) { - externalInfo.hash = element.getTreeHash() - } - return element + private inner class SchemeDataHolderImpl(private val bytes: ByteArray, private val externalInfo: ExternalInfo) : SchemeDataHolder { + override fun read() = loadElement(bytes.inputStream()) + + override fun updateDigest() { + schemeToInfo.forEachEntry({ k, v -> + if (v !== externalInfo) { + return@forEachEntry true + } + + @Suppress("UNCHECKED_CAST") + try { + externalInfo.digest = (processor.writeScheme(k as MUTABLE_SCHEME) as Element).digest() + } + catch (e: WriteExternalException) { + LOG.error("Cannot update digest", e) + } + false + }) } } @@ -395,7 +409,7 @@ class SchemeManagerImpl(val fileSpec: String, fun createInfo(schemeName: String, element: Element?): ExternalInfo { val info = ExternalInfo(fileNameWithoutExtension, extension) element?.let { - info.hash = it.getTreeHash() + info.digest = it.digest() } info.schemeName = schemeName return info @@ -558,14 +572,14 @@ class SchemeManagerImpl(val fileSpec: String, fileNameWithoutExtension = nameGenerator.generateUniqueName(FileUtil.sanitizeFileName(scheme.name, false)) } - val newHash = element!!.getTreeHash() - if (externalInfo != null && currentFileNameWithoutExtension === fileNameWithoutExtension && newHash == externalInfo.hash) { + val newDigest = element!!.digest() + if (externalInfo != null && currentFileNameWithoutExtension === fileNameWithoutExtension && externalInfo.isDigestEquals(newDigest)) { return } // save only if scheme differs from bundled val bundledScheme = readOnlyExternalizableSchemes.get(scheme.name) - if (bundledScheme != null && schemeToInfo.get(bundledScheme)?.hash == newHash) { + if (bundledScheme != null && schemeToInfo.get(bundledScheme)?.isDigestEquals(newDigest) ?: false) { externalInfo?.scheduleDelete() return } @@ -639,7 +653,7 @@ class SchemeManagerImpl(val fileSpec: String, else { externalInfo.setFileNameWithoutExtension(fileNameWithoutExtension, schemeExtension) } - externalInfo.hash = newHash + externalInfo.digest = newDigest externalInfo.schemeName = scheme.name } @@ -889,7 +903,8 @@ class SchemeManagerImpl(val fileSpec: String, private class ExternalInfo(var fileNameWithoutExtension: String, var fileExtension: String?) { // we keep it to detect rename var schemeName: String? = null - var hash = 0 + + var digest: ByteArray? = null val fileName: String get() = "$fileNameWithoutExtension$fileExtension" @@ -899,6 +914,8 @@ class SchemeManagerImpl(val fileSpec: String, fileExtension = extension } + fun isDigestEquals(newDigest: ByteArray) = Arrays.equals(digest, newDigest) + override fun toString() = fileName } @@ -930,4 +947,25 @@ fun createDir(ioDir: Path, requestor: Any): VirtualFile { fun getFile(fileName: String, parent: VirtualFile, requestor: Any): VirtualFile { return parent.findChild(fileName) ?: runWriteAction { parent.createChildData(requestor, fileName) } +} + +class DigestOutputStream(val digest: MessageDigest) : OutputStream() { + override fun write(b: Int) { + digest.update(b.toByte()) + } + + override fun write(b: ByteArray, off: Int, len: Int) { + digest.update(b, off, len) + } + + override fun toString(): String { + return "[Digest Output Stream] " + digest.toString() + } +} + +fun Element.digest(): ByteArray { + // sha-1 is enough, sha-256 is slower, see https://www.nayuki.io/page/native-hash-functions-for-java + val digest = MessageDigest.getInstance("SHA-1") + serializeElementToBinary(this, DigestOutputStream(digest)) + return digest.digest() } \ No newline at end of file diff --git a/platform/configuration-store-impl/src/StateMap.kt b/platform/configuration-store-impl/src/StateMap.kt index 6d8171a0a1b7..80d1d22f7e67 100644 --- a/platform/configuration-store-impl/src/StateMap.kt +++ b/platform/configuration-store-impl/src/StateMap.kt @@ -32,7 +32,7 @@ import java.util.concurrent.atomic.AtomicReferenceArray fun archiveState(state: Element): BufferExposingByteArrayOutputStream { val byteOut = BufferExposingByteArrayOutputStream() SnappyOutputStream(byteOut).use { - writeElement(state, it) + serializeElementToBinary(state, it) } return byteOut } diff --git a/platform/configuration-store-impl/testSrc/BinaryXmlOutputterTest.kt b/platform/configuration-store-impl/testSrc/BinaryXmlOutputterTest.kt index 3e178eacfaab..d15acabe782d 100644 --- a/platform/configuration-store-impl/testSrc/BinaryXmlOutputterTest.kt +++ b/platform/configuration-store-impl/testSrc/BinaryXmlOutputterTest.kt @@ -18,7 +18,7 @@ class BinaryXmlOutputterTest { private fun test(xml: String) { val byteOut = BufferExposingByteArrayOutputStream() byteOut.use { - writeElement(loadElement(xml), it) + serializeElementToBinary(loadElement(xml), it) } val xmlAfter = JDOMUtil.writeElement(byteOut.toByteArray().inputStream().use { readElement(it) }) diff --git a/platform/core-api/src/com/intellij/openapi/options/scheme.kt b/platform/core-api/src/com/intellij/openapi/options/scheme.kt index 6df367a18764..b28299a033fa 100644 --- a/platform/core-api/src/com/intellij/openapi/options/scheme.kt +++ b/platform/core-api/src/com/intellij/openapi/options/scheme.kt @@ -46,9 +46,13 @@ interface SchemeExtensionProvider { val isUpgradeNeeded: Boolean } - interface SchemeDataHolder { + /** + * You should call updateDigest() after read on init. + */ fun read(): Element + + fun updateDigest() } abstract class SchemeManagerFactory { diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionProfileManagerImpl.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionProfileManagerImpl.java index 5e0d2fa26064..21695eb50c2f 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionProfileManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionProfileManagerImpl.java @@ -193,11 +193,9 @@ public class InspectionProfileManagerImpl extends InspectionProfileManager imple } private void createDefaultProfile() { - final InspectionProfileImpl defaultProfile = createSampleProfile(InspectionProfileImpl.DEFAULT_PROFILE_NAME, getDefaultProfile()); - addProfile(defaultProfile); + addProfile(createSampleProfile(InspectionProfileImpl.DEFAULT_PROFILE_NAME, getDefaultProfile())); } - @Override public Profile loadProfile(@NotNull String path) throws IOException, JDOMException { final File file = new File(path); diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/InspectionToolsConfigurable.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/InspectionToolsConfigurable.java index a78b00703237..b1ec97958987 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/InspectionToolsConfigurable.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/InspectionToolsConfigurable.java @@ -353,13 +353,13 @@ public abstract class InspectionToolsConfigurable extends BaseConfigurable final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); descriptor.setDescription("Choose directory to store profile file"); FileChooser.chooseFile(descriptor, getProject(), wholePanel, null, file -> { - final Element element = new Element("inspections"); + final Element element; try { final SingleInspectionProfilePanel panel = getSelectedPanel(); LOG.assertTrue(panel != null); final InspectionProfileImpl profile = getSelectedObject(); LOG.assertTrue(true); - profile.writeExternal(element); + element = profile.writeExternal(); final String filePath = FileUtil.toSystemDependentName(file.getPath()) + File.separator + FileUtil.sanitizeFileName(profile.getName()) + ".xml"; if (new File(filePath).isFile()) { diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemeImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemeImpl.java index 605e5a375116..eecfddf1d415 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemeImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemeImpl.java @@ -84,10 +84,12 @@ public class CodeStyleSchemeImpl implements CodeStyleScheme, ExternalizableSchem @Override public CodeStyleSettings getCodeStyleSettings() { - if (myDataHolder != null) { - init(myParentSchemeName == null ? null : CodeStyleSchemesImpl.getSchemeManager().findSchemeByName(myParentSchemeName), myDataHolder.read()); - myParentSchemeName = null; + SchemeDataHolder dataHolder = myDataHolder; + if (dataHolder != null) { myDataHolder = null; + init(myParentSchemeName == null ? null : CodeStyleSchemesImpl.getSchemeManager().findSchemeByName(myParentSchemeName), dataHolder.read()); + dataHolder.updateDigest(); + myParentSchemeName = null; } return myCodeStyleSettings; } diff --git a/plugins/copyright/src/com/maddyhome/idea/copyright/CopyrightManager.java b/plugins/copyright/src/com/maddyhome/idea/copyright/CopyrightManager.java index ba9e0de84874..856c6ae681cc 100644 --- a/plugins/copyright/src/com/maddyhome/idea/copyright/CopyrightManager.java +++ b/plugins/copyright/src/com/maddyhome/idea/copyright/CopyrightManager.java @@ -107,7 +107,7 @@ public class CopyrightManager extends AbstractProjectComponent implements Persis } @NonNls - private static final String COPYRIGHT = "copyright"; + static final String COPYRIGHT = "copyright"; @NonNls private static final String MODULE2COPYRIGHT = "module2copyright"; @NonNls @@ -135,8 +135,7 @@ public class CopyrightManager extends AbstractProjectComponent implements Persis try { if (!myCopyrights.isEmpty()) { for (CopyrightProfile copyright : myCopyrights.values()) { - Element copyrightElement = new Element(COPYRIGHT); - copyright.writeExternal(copyrightElement); + Element copyrightElement = copyright.writeExternal(); if (!JDOMUtil.isEmpty(copyrightElement)) { state.addContent(copyrightElement); } @@ -291,7 +290,7 @@ public class CopyrightManager extends AbstractProjectComponent implements Persis @NotNull @Override protected String getSubStateTagName() { - return "copyright"; + return COPYRIGHT; } } } diff --git a/plugins/copyright/src/com/maddyhome/idea/copyright/CopyrightProfile.java b/plugins/copyright/src/com/maddyhome/idea/copyright/CopyrightProfile.java index b0f5c50d64fb..957bbbc85c3e 100644 --- a/plugins/copyright/src/com/maddyhome/idea/copyright/CopyrightProfile.java +++ b/plugins/copyright/src/com/maddyhome/idea/copyright/CopyrightProfile.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * 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. @@ -18,6 +18,8 @@ package com.maddyhome.idea.copyright; import com.intellij.profile.ProfileEx; import com.intellij.util.xmlb.SmartSerializer; import com.maddyhome.idea.copyright.pattern.EntityUtil; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; public class CopyrightProfile extends ProfileEx { @SuppressWarnings("SpellCheckingInspection") @@ -64,4 +66,12 @@ public class CopyrightProfile extends ProfileEx { public void setAllowReplaceKeyword(String allowReplaceKeyword) { this.allowReplaceKeyword = allowReplaceKeyword; } + + @NotNull + @Override + public Element writeExternal() { + Element result = new Element(CopyrightManager.COPYRIGHT); + serializeInto(result, true); + return result; + } }