InspectionSchemeTest

This commit is contained in:
Vladimir Krivosheev
2016-06-07 12:11:47 +02:00
parent 20f3ab0cd8
commit d6595ddcf7
19 changed files with 249 additions and 134 deletions
+1
View File
@@ -53,5 +53,6 @@
<orderEntry type="module" module-name="xml-psi-impl" />
<orderEntry type="library" scope="TEST" name="KotlinTest" level="project" />
<orderEntry type="module" module-name="built-in-server-api" scope="TEST" />
<orderEntry type="module" module-name="configuration-store-impl" scope="TEST" />
</component>
</module>
@@ -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("<inspections version=\"1.0\">\n" +
" <option name=\"myName\" value=\"ToConvert\" />\n" +
" <inspection_tool class=\"JavaDoc\" enabled=\"false\" level=\"WARNING\" enabled_by_default=\"false\">\n" +
" <option name=\"TOP_LEVEL_CLASS_OPTIONS\">\n" +
" <value>\n" +
" <option name=\"ACCESS_JAVADOC_REQUIRED_FOR\" value=\"none\" />\n" +
" <option name=\"REQUIRED_TAGS\" value=\"\" />\n" +
" </value>\n" +
" </option>\n" +
" <option name=\"INNER_CLASS_OPTIONS\">\n" +
" <value>\n" +
" <option name=\"ACCESS_JAVADOC_REQUIRED_FOR\" value=\"none\" />\n" +
" <option name=\"REQUIRED_TAGS\" value=\"\" />\n" +
" </value>\n" +
" </option>\n" +
" <option name=\"METHOD_OPTIONS\">\n" +
" <value>\n" +
" <option name=\"ACCESS_JAVADOC_REQUIRED_FOR\" value=\"none\" />\n" +
" <option name=\"REQUIRED_TAGS\" value=\"@return@param@throws or @exception\" />\n" +
" </value>\n" + " </option>\n" +
" <option name=\"FIELD_OPTIONS\">\n" +
" <value>\n" +
" <option name=\"ACCESS_JAVADOC_REQUIRED_FOR\" value=\"none\" />\n" +
" <option name=\"REQUIRED_TAGS\" value=\"\" />\n" +
" </value>\n" +
" </option>\n" +
" <option name=\"IGNORE_DEPRECATED\" value=\"false\" />\n" +
" <option name=\"IGNORE_JAVADOC_PERIOD\" value=\"false\" />\n" +
" <option name=\"IGNORE_DUPLICATED_THROWS\" value=\"false\" />\n" +
" <option name=\"IGNORE_POINT_TO_ITSELF\" value=\"false\" />\n" +
" <option name=\"myAdditionalJavadocTags\" value=\"tag1,tag2 \" />\n" +
" </inspection_tool>\n" +
"</inspections>").getRootElement();
return JdomKt.loadElement("<profile version=\"1.0\">\n" +
" <option name=\"myName\" value=\"ToConvert\" />\n" +
" <inspection_tool class=\"JavaDoc\" enabled=\"false\" level=\"WARNING\" enabled_by_default=\"false\">\n" +
" <option name=\"TOP_LEVEL_CLASS_OPTIONS\">\n" +
" <value>\n" +
" <option name=\"ACCESS_JAVADOC_REQUIRED_FOR\" value=\"none\" />\n" +
" <option name=\"REQUIRED_TAGS\" value=\"\" />\n" +
" </value>\n" +
" </option>\n" +
" <option name=\"INNER_CLASS_OPTIONS\">\n" +
" <value>\n" +
" <option name=\"ACCESS_JAVADOC_REQUIRED_FOR\" value=\"none\" />\n" +
" <option name=\"REQUIRED_TAGS\" value=\"\" />\n" +
" </value>\n" +
" </option>\n" +
" <option name=\"METHOD_OPTIONS\">\n" +
" <value>\n" +
" <option name=\"ACCESS_JAVADOC_REQUIRED_FOR\" value=\"none\" />\n" +
" <option name=\"REQUIRED_TAGS\" value=\"@return@param@throws or @exception\" />\n" +
" </value>\n" + " </option>\n" +
" <option name=\"FIELD_OPTIONS\">\n" +
" <value>\n" +
" <option name=\"ACCESS_JAVADOC_REQUIRED_FOR\" value=\"none\" />\n" +
" <option name=\"REQUIRED_TAGS\" value=\"\" />\n" +
" </value>\n" +
" </option>\n" +
" <option name=\"IGNORE_DEPRECATED\" value=\"false\" />\n" +
" <option name=\"IGNORE_JAVADOC_PERIOD\" value=\"false\" />\n" +
" <option name=\"IGNORE_DUPLICATED_THROWS\" value=\"false\" />\n" +
" <option name=\"IGNORE_POINT_TO_ITSELF\" value=\"false\" />\n" +
" <option name=\"myAdditionalJavadocTags\" value=\"tag1,tag2 \" />\n" +
" </inspection_tool>\n" +
"</profile>");
}
public void testReloadProfileWithUnknownScopes() throws Exception {
final Element element = JDOMUtil.loadDocument("<inspections version=\"1.0\">\n" +
final Element element = JdomKt.loadElement("<profile version=\"1.0\">\n" +
" <option name=\"myName\" value=\"" + PROFILE + "\" />\n" +
" <inspection_tool class=\"ArgNamesErrorsInspection\" enabled=\"true\" level=\"ERROR\" enabled_by_default=\"false\" />\n" +
" <inspection_tool class=\"ArgNamesWarningsInspection\" enabled=\"true\" level=\"WARNING\" enabled_by_default=\"false\" />\n" +
@@ -199,32 +196,28 @@ public class InspectionProfileTest extends LightIdeaTestCase {
" <inspection_tool class=\"UNUSED_IMPORT\" enabled=\"true\" level=\"WARNING\" enabled_by_default=\"true\">\n" +
" <scope name=\"Unknown scope name\" level=\"WARNING\" enabled=\"true\" />\n" +
" </inspection_tool>\n" +
"</inspections>").getRootElement();
"</profile>");
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("<inspections version=\"1.0\">\n" +
final Element element = JdomKt.loadElement("<profile version=\"1.0\">\n" +
" <option name=\"myName\" value=\"" + PROFILE + "\" />\n" +
"</inspections>").getRootElement();
"</profile>");
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("<inspections version=\"1.0\">\n" +
final Element unusedProfile = JdomKt.loadElement("<profile version=\"1.0\">\n" +
" <option name=\"myName\" value=\"" + PROFILE + "\" />\n" +
" <inspection_tool class=\"UNUSED_SYMBOL\" enabled=\"true\" level=\"WARNING\" enabled_by_default=\"false\">\n" +
" <option name=\"LOCAL_VARIABLE\" value=\"true\" />\n" +
@@ -240,7 +233,7 @@ public class InspectionProfileTest extends LightIdeaTestCase {
" <option name=\"ADD_SERVLET_TO_ENTRIES\" value=\"true\" />\n" +
" <option name=\"ADD_NONJAVA_TO_ENTRIES\" value=\"false\" />\n" +
" </inspection_tool>\n" +
"</inspections>").getRootElement();
"</profile>");
profile.readExternal(unusedProfile);
model = profile.getModifiableModel();
model.commit();
@@ -292,8 +285,7 @@ public class InspectionProfileTest extends LightIdeaTestCase {
"</profile>";
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 {
"</profile>",
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<InspectionToolWrapper> initializedTools = getInitializedTools(profile);
if (initializedTools.size() > 0) {
for (InspectionToolWrapper initializedTool : initializedTools) {
@@ -543,9 +528,7 @@ public class InspectionProfileTest extends LightIdeaTestCase {
"</profile>";
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) {
@@ -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 = """
<inspections profile_name="Bar" version="1.0">
<option name="myName" value="Bar" />
<inspection_tool class="Since15" enabled="true" level="ERROR" enabled_by_default="true" />
"</inspections>""".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
}
}
@@ -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<String, Profile> myProfiles = new THashMap<String, Profile>();
private final Map<String, Profile> myProfiles = new THashMap<>();
protected final DependencyValidationManager myHolder;
private final List<ProfileChangeAdapter> 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()) {
@@ -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();
}
@@ -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<HighlightSeverity> 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<String> graphGenerator = GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph<String>() {
if (dataHolder != null) {
dataHolder.updateDigest();
}
DFSTBuilder<String> builder = new DFSTBuilder<>(GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph<String>() {
@Override
public Collection<String> getNodes() {
return dependencies.keySet();
@@ -575,9 +591,7 @@ public class InspectionProfileImpl extends ProfileEx implements ModifiableModel,
public Iterator<String> getIn(String n) {
return dependencies.get(n).iterator();
}
}));
DFSTBuilder<String> builder = new DFSTBuilder<>(graphGenerator);
})));
if (builder.isAcyclic()) {
final List<String> scopes = builder.getSortedNodes();
myScopesOrder = ArrayUtil.toStringArray(scopes);
@@ -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);
}
}
@@ -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() {
@@ -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)) {
@@ -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<SchemeManagerImpl<Scheme, out Scheme>>()
abstract val componentManager: ComponentManager
protected open val componentManager: ComponentManager? = null
override final fun <T : Scheme, MutableT : T> create(directoryName: String, processor: SchemeProcessor<T, MutableT>, presentableName: String?, roamingType: RoamingType): SchemeManager<T> {
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<Scheme, out Scheme>)
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<Scheme, out Scheme>) -> 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)
}
}
@@ -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<T : Scheme, MUTABLE_SCHEME : T>(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<T : Scheme, MUTABLE_SCHEME : T>(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<T : Scheme, MUTABLE_SCHEME : T>(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<T : Scheme, MUTABLE_SCHEME : T>(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<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
else {
externalInfo.setFileNameWithoutExtension(fileNameWithoutExtension, schemeExtension)
}
externalInfo.hash = newHash
externalInfo.digest = newDigest
externalInfo.schemeName = scheme.name
}
@@ -889,7 +903,8 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(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<T : Scheme, MUTABLE_SCHEME : T>(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()
}
@@ -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
}
@@ -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) })
@@ -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 {
@@ -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);
@@ -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()) {
@@ -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;
}
@@ -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;
}
}
}
@@ -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;
}
}