From 40f2d3fd6af7d11184fb2e521f76e69be4c55d0a Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Sat, 5 Jan 2019 13:06:00 +0100 Subject: [PATCH] IDEA-CR-41536 1. clear string interner on close because thread reused later as part of application pool 2. doesn't make a lot of sense to intern class names --- .../ide/plugins/IdeaPluginDescriptorImpl.java | 10 +- .../ide/plugins/LoadDescriptorsContext.java | 119 +++++++++++++++--- .../ide/plugins/PluginManagerCore.java | 12 +- .../com/intellij/openapi/util/JDOMUtil.java | 22 ++-- .../openapi/util/JdomInternFactory.java | 60 --------- .../openapi/util/SafeJdomFactory.java | 55 ++++++++ .../openapi/util/SafeStAXStreamBuilder.java | 46 +++---- .../intellij/util/containers/Interner.java | 22 ++-- 8 files changed, 199 insertions(+), 147 deletions(-) delete mode 100644 platform/util/src/com/intellij/openapi/util/JdomInternFactory.java create mode 100644 platform/util/src/com/intellij/openapi/util/SafeJdomFactory.java diff --git a/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.java b/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.java index 91e69c4df245..c6dff6eac58f 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.java +++ b/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.java @@ -13,17 +13,13 @@ import com.intellij.openapi.extensions.ExtensionPoint; import com.intellij.openapi.extensions.ExtensionsArea; import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.extensions.impl.ExtensionsAreaImpl; -import com.intellij.openapi.util.InvalidDataException; -import com.intellij.openapi.util.JDOMUtil; -import com.intellij.openapi.util.NullableLazyValue; -import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.util.text.StringUtilRt; import com.intellij.util.ObjectUtils; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; -import com.intellij.util.containers.StringInterner; import com.intellij.util.xmlb.BeanBinding; import com.intellij.util.xmlb.JDOMXIncluder; import com.intellij.util.xmlb.XmlSerializer; @@ -145,8 +141,8 @@ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor { readExternal(element); } - public void loadFromFile(@NotNull File file, @Nullable StringInterner stringInterner) throws IOException, JDOMException { - readExternal(JDOMUtil.load(file, stringInterner), file.toURI().toURL(), JDOMXIncluder.DEFAULT_PATH_RESOLVER); + public void loadFromFile(@NotNull File file, @Nullable SafeJdomFactory factory) throws IOException, JDOMException { + readExternal(JDOMUtil.load(file, factory), file.toURI().toURL(), JDOMXIncluder.DEFAULT_PATH_RESOLVER); } // used in upsource diff --git a/platform/core-impl/src/com/intellij/ide/plugins/LoadDescriptorsContext.java b/platform/core-impl/src/com/intellij/ide/plugins/LoadDescriptorsContext.java index fff78b7f9c9b..d218cfcd6b8d 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/LoadDescriptorsContext.java +++ b/platform/core-impl/src/com/intellij/ide/plugins/LoadDescriptorsContext.java @@ -1,32 +1,53 @@ // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.ide.plugins; -import com.intellij.concurrency.JobSchedulerImpl; +import com.intellij.openapi.util.SafeJdomFactory; +import com.intellij.util.SmartList; import com.intellij.util.concurrency.AppExecutorUtil; -import com.intellij.util.containers.StringInterner; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.Interner; +import org.jdom.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.concurrent.ExecutorService; -final class LoadDescriptorsContext { +final class LoadDescriptorsContext implements AutoCloseable { private final ExecutorService myExecutorService; private final PluginLoadProgressManager myPluginLoadProgressManager; + private final Collection> myInterners; + // synchronization will ruin parallel loading, so, string pool is local per thread - private final ThreadLocal myThreadLocalStringInterner = ThreadLocal.withInitial(() -> new StringInterner() { - @NotNull - @Override - public String intern(@NotNull String name) { - // doesn't make any sense to intern long texts (JdomInternFactory doesn't intern CDATA, but plugin description can be simply Text) - return name.length() < 64 ? super.intern(name) : name; - } - }); + private final ThreadLocal myThreadLocalXmlFactory; LoadDescriptorsContext(@Nullable PluginLoadProgressManager pluginLoadProgressManager, boolean isParallel) { myPluginLoadProgressManager = pluginLoadProgressManager; - int maxThreads = isParallel ? JobSchedulerImpl.getCPUCoresCount() : 1; - myExecutorService = maxThreads > 1 ? AppExecutorUtil.createBoundedApplicationPoolExecutor("PluginManager Loader", maxThreads) : null; + int maxThreads = isParallel ? (Runtime.getRuntime().availableProcessors() - 1) : 1; + if (maxThreads > 1) { + myExecutorService = AppExecutorUtil.createBoundedApplicationPoolExecutor("PluginManager Loader", maxThreads); + myInterners = Collections.newSetFromMap(ContainerUtil.newConcurrentMap(maxThreads)); + } + else { + myExecutorService = null; + myInterners = new SmartList<>(); + } + + myThreadLocalXmlFactory = ThreadLocal.withInitial(() -> { + Interner interner = new Interner(Arrays.asList(PluginXmlFactory.CLASS_NAMES)) { + @NotNull + @Override + public String intern(@NotNull String name) { + // doesn't make any sense to intern long texts (JdomInternFactory doesn't intern CDATA, but plugin description can be simply Text) + return name.length() < 64 ? super.intern(name) : name; + } + }; + myInterners.add(interner); + return new PluginXmlFactory(interner); + }); } @Nullable @@ -40,7 +61,75 @@ final class LoadDescriptorsContext { } @Nullable - public StringInterner getStringInterner() { - return myThreadLocalStringInterner.get(); + public SafeJdomFactory getXmlFactory() { + return myThreadLocalXmlFactory.get(); + } + + @Override + public void close() { + if (myExecutorService == null) { + myThreadLocalXmlFactory.remove(); + return; + } + + myExecutorService.submit(() -> { + for (Interner interner : myInterners) { + interner.clear(); + } + }); + myExecutorService.shutdown(); + } + + /** + * Consider using some threshold in StringInterner - CDATA is not interned at all, + * but maybe some long text for Text node doesn't make sense to intern too. + */ + // don't intern CDATA - in most cases it is used for some unique large text (e.g. plugin description) + private final static class PluginXmlFactory extends SafeJdomFactory.BaseSafeJdomFactory { + // ouch, do we really cannot agree how to name implementation class attribute? + private static final String[] CLASS_NAMES = new String[]{ + "implementation-class", "implementation", "implementationClass", "serviceImplementation", "class", "className", "beanClass", + "serviceInterface", "interface", "interfaceClass", "instance", + "qualifiedName", + }; + + private final Interner stringInterner; + + PluginXmlFactory(@NotNull Interner stringInterner) { + this.stringInterner = stringInterner; + + for (int i = 0; i < CLASS_NAMES.length; i++) { + CLASS_NAMES[i] = stringInterner.intern(CLASS_NAMES[i]); + } + } + + @NotNull + @Override + public Element element(@NotNull String name, @Nullable Namespace namespace) { + return super.element(stringInterner.intern(name), namespace); + } + + @NotNull + @Override + public Attribute attribute(@NotNull String name, @NotNull String value, @Nullable AttributeType type, @Nullable Namespace namespace) { + String internedName = stringInterner.intern(name); + for (String s : CLASS_NAMES) { + if (internedName == s) { + return super.attribute(internedName, value, type, namespace); + } + } + return super.attribute(internedName, stringInterner.intern(value), type, namespace); + } + + @NotNull + @Override + public Text text(@NotNull String text, @NotNull Element parentElement) { + if (parentElement.getName() == "className" || parentElement.getName() == "implementation-class") { + return super.text(text, parentElement); + } + else { + return super.text(stringInterner.intern(text), parentElement); + } + } } } diff --git a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java index 5931b99a5d69..e302fb74d362 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java +++ b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java @@ -26,7 +26,6 @@ import com.intellij.util.*; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.JBIterable; import com.intellij.util.containers.MultiMap; -import com.intellij.util.containers.StringInterner; import com.intellij.util.execution.ParametersListUtil; import com.intellij.util.graph.*; import com.intellij.util.io.URLUtil; @@ -646,7 +645,7 @@ public class PluginManagerCore { try { IdeaPluginDescriptorImpl descriptor = new IdeaPluginDescriptorImpl(notNull(pluginPath, file), loadingContext.isBundled); - descriptor.loadFromFile(descriptorFile, loadingContext.getStringInterner()); + descriptor.loadFromFile(descriptorFile, loadingContext.getXmlFactory()); return descriptor; } catch (XmlSerializationException | JDOMException | IOException e) { @@ -681,7 +680,7 @@ public class PluginManagerCore { ZipEntry entry = zipFile.getEntry(entryName); if (entry != null) { IdeaPluginDescriptorImpl descriptor = new IdeaPluginDescriptorImpl(notNull(pluginPath, file), context.isBundled); - descriptor.readExternal(JDOMUtil.load(zipFile.getInputStream(entry), context.getStringInterner()), jarURL, pathResolver); + descriptor.readExternal(JDOMUtil.load(zipFile.getInputStream(entry), context.getXmlFactory()), jarURL, pathResolver); context.myLastZipFileContainingDescriptor = file; return descriptor; } @@ -745,11 +744,11 @@ public class PluginManagerCore { } @Nullable - public StringInterner getStringInterner() { + public SafeJdomFactory getXmlFactory() { if (myParentContext == null) { return null; } - return myParentContext.getStringInterner(); + return myParentContext.getXmlFactory(); } } @@ -1205,8 +1204,7 @@ public class PluginManagerCore { URL platformPluginURL = computePlatformPluginUrlAndCollectPluginUrls(PluginManagerCore.class.getClassLoader(), urlsFromClassPath); PluginLoadProgressManager pluginLoadProgressManager = progress == null ? null : new PluginLoadProgressManager(progress, urlsFromClassPath.size()); - LoadDescriptorsContext context = new LoadDescriptorsContext(pluginLoadProgressManager, SystemProperties.getBooleanProperty("parallel.pluginDescriptors.loading", true)); - try { + try (LoadDescriptorsContext context = new LoadDescriptorsContext(pluginLoadProgressManager, SystemProperties.getBooleanProperty("parallel.pluginDescriptors.loading", true))) { loadDescriptorsFromDir(new File(PathManager.getPluginsPath()), result, false, context); Application application = ApplicationManager.getApplication(); if (application == null || !application.isUnitTestMode()) { diff --git a/platform/util/src/com/intellij/openapi/util/JDOMUtil.java b/platform/util/src/com/intellij/openapi/util/JDOMUtil.java index 170d2a57616f..699268171250 100644 --- a/platform/util/src/com/intellij/openapi/util/JDOMUtil.java +++ b/platform/util/src/com/intellij/openapi/util/JDOMUtil.java @@ -6,7 +6,6 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.StringInterner; import com.intellij.util.io.URLUtil; import com.intellij.util.text.CharArrayUtil; import com.intellij.util.text.CharSequenceReader; @@ -15,6 +14,7 @@ import org.jdom.*; import org.jdom.filter.Filter; import org.jdom.output.Format; import org.jdom.output.XMLOutputter; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -215,11 +215,11 @@ public class JDOMUtil { } @NotNull - private static Element loadUsingStaX(@NotNull Reader reader, @Nullable StringInterner stringInterner) throws JDOMException, IOException { + private static Element loadUsingStaX(@NotNull Reader reader, @Nullable SafeJdomFactory factory) throws JDOMException, IOException { try { XMLStreamReader xmlStreamReader = XML_INPUT_FACTORY.getValue().createXMLStreamReader(reader); try { - return SafeStAXStreamBuilder.build(xmlStreamReader, true, stringInterner); + return SafeStAXStreamBuilder.build(xmlStreamReader, true, factory == null ? SafeStAXStreamBuilder.FACTORY : factory); } finally { xmlStreamReader.close(); @@ -271,12 +271,12 @@ public class JDOMUtil { } /** - * Consider using some threshold in StringInterner - CDATA is not interned at all, - * but maybe some long text for Text node doesn't make sense to intern too. + * Internal use only. */ + @ApiStatus.Experimental @NotNull - public static Element load(@NotNull File file, @Nullable StringInterner stringInterner) throws JDOMException, IOException { - return loadUsingStaX(new BufferedReader(new InputStreamReader(new FileInputStream(file), CharsetToolkit.UTF8_CHARSET)), stringInterner); + public static Element load(@NotNull File file, @Nullable SafeJdomFactory factory) throws JDOMException, IOException { + return loadUsingStaX(new BufferedReader(new InputStreamReader(new FileInputStream(file), CharsetToolkit.UTF8_CHARSET)), factory); } /** @@ -301,12 +301,12 @@ public class JDOMUtil { } /** - * Consider using some threshold in StringInterner - CDATA is not interned at all, - * but maybe some long text for Text node doesn't make sense to intern too. + * Internal use only. */ + @ApiStatus.Experimental @NotNull - public static Element load(@NotNull InputStream stream, @Nullable StringInterner stringInterner) throws JDOMException, IOException { - return loadUsingStaX(new InputStreamReader(stream, CharsetToolkit.UTF8_CHARSET), stringInterner); + public static Element load(@NotNull InputStream stream, @Nullable SafeJdomFactory factory) throws JDOMException, IOException { + return loadUsingStaX(new InputStreamReader(stream, CharsetToolkit.UTF8_CHARSET), factory); } @NotNull diff --git a/platform/util/src/com/intellij/openapi/util/JdomInternFactory.java b/platform/util/src/com/intellij/openapi/util/JdomInternFactory.java deleted file mode 100644 index 4fc9dd00287d..000000000000 --- a/platform/util/src/com/intellij/openapi/util/JdomInternFactory.java +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.intellij.openapi.util; - -import com.intellij.util.containers.StringInterner; -import org.jdom.*; -import org.jetbrains.annotations.NotNull; - -// don't intern CDATA - in most cases it is used for some unique large text (e.g. plugin description) -final class JdomInternFactory extends DefaultJDOMFactory { - private final StringInterner stringInterner; - - JdomInternFactory(@NotNull StringInterner stringInterner) { - this.stringInterner = stringInterner; - } - - @Override - public Attribute attribute(String name, String value, Namespace namespace) { - return super.attribute(stringInterner.intern(name), stringInterner.intern(value), namespace); - } - - @Override - public Attribute attribute(String name, String value) { - return super.attribute(stringInterner.intern(name), stringInterner.intern(value)); - } - - @Override - public Attribute attribute(String name, String value, AttributeType type) { - return super.attribute(stringInterner.intern(name), stringInterner.intern(value), type); - } - - @Override - public Attribute attribute(String name, String value, AttributeType type, Namespace namespace) { - return super.attribute(stringInterner.intern(name), stringInterner.intern(value), type, namespace); - } - - @Override - public Text text(int line, int col, String text) { - return super.text(line, col, stringInterner.intern(text)); - } - - @Override - public Element element(int line, int col, String name, Namespace namespace) { - return super.element(line, col, stringInterner.intern(name), namespace); - } - - @Override - public Element element(int line, int col, String name) { - return super.element(line, col, stringInterner.intern(name)); - } - - @Override - public Element element(int line, int col, String name, String uri) { - return super.element(line, col, stringInterner.intern(name), uri); - } - - @Override - public Element element(int line, int col, String name, String prefix, String uri) { - return super.element(line, col, stringInterner.intern(name), prefix, uri); - } -} diff --git a/platform/util/src/com/intellij/openapi/util/SafeJdomFactory.java b/platform/util/src/com/intellij/openapi/util/SafeJdomFactory.java new file mode 100644 index 000000000000..f54e72f6db57 --- /dev/null +++ b/platform/util/src/com/intellij/openapi/util/SafeJdomFactory.java @@ -0,0 +1,55 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.openapi.util; + +import org.jdom.*; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal use only. + */ +@ApiStatus.Experimental +public interface SafeJdomFactory { + @NotNull + Element element(@NotNull String name, @Nullable Namespace namespace); + + @NotNull + Attribute attribute(@NotNull String name, @NotNull String value, @Nullable AttributeType type, @Nullable Namespace namespace); + + @NotNull + Text text(@NotNull String text, @NotNull Element parentElement); + + @NotNull + CDATA cdata(@NotNull String text); + + class BaseSafeJdomFactory implements SafeJdomFactory { + @NotNull + @Override + public Element element(@NotNull String name, @Nullable Namespace namespace) { + Element element = new Element(name, namespace); + if (namespace != null) { + element.setNamespace(namespace); + } + return element; + } + + @NotNull + @Override + public Attribute attribute(@NotNull String name, @NotNull String value, @Nullable AttributeType type, @Nullable Namespace namespace) { + return new Attribute(name, value, type, namespace); + } + + @NotNull + @Override + public Text text(@NotNull String text, @NotNull Element parentElement) { + return new Text(text); + } + + @NotNull + @Override + public CDATA cdata(@NotNull String text) { + return new CDATA(text); + } + } +} diff --git a/platform/util/src/com/intellij/openapi/util/SafeStAXStreamBuilder.java b/platform/util/src/com/intellij/openapi/util/SafeStAXStreamBuilder.java index f48d2fcdbc8d..0cad2c151c24 100644 --- a/platform/util/src/com/intellij/openapi/util/SafeStAXStreamBuilder.java +++ b/platform/util/src/com/intellij/openapi/util/SafeStAXStreamBuilder.java @@ -1,10 +1,8 @@ // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.openapi.util; -import com.intellij.util.containers.StringInterner; import org.jdom.*; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; @@ -13,16 +11,16 @@ import static javax.xml.stream.XMLStreamConstants.*; // DTD, COMMENT and PROCESSING_INSTRUCTION wi final class SafeStAXStreamBuilder { - private static final JDOMFactory factory = new DefaultJDOMFactory(); + static final SafeJdomFactory FACTORY = new SafeJdomFactory.BaseSafeJdomFactory(); - static Document buildDocument(@NotNull XMLStreamReader stream, boolean isIgnoreBoundaryWhitespace) throws JDOMException, XMLStreamException { + static Document buildDocument(@NotNull XMLStreamReader stream, @SuppressWarnings("SameParameterValue") boolean isIgnoreBoundaryWhitespace) throws JDOMException, XMLStreamException { int state = stream.getEventType(); if (START_DOCUMENT != state) { throw new JDOMException("JDOM requires that XMLStreamReaders are at their beginning when being processed."); } - final Document document = factory.document(null); + final Document document = new Document(); while (state != END_DOCUMENT) { switch (state) { @@ -35,22 +33,15 @@ final class SafeStAXStreamBuilder { break; case DTD: + case COMMENT: + case PROCESSING_INSTRUCTION: + case SPACE: break; case START_ELEMENT: - document.setRootElement(processElementFragment(stream, isIgnoreBoundaryWhitespace, factory)); + document.setRootElement(processElementFragment(stream, isIgnoreBoundaryWhitespace, FACTORY)); break; - case END_ELEMENT: - throw new JDOMException("Unexpected XMLStream event at Document level: END_ELEMENT"); - case ENTITY_REFERENCE: - throw new JDOMException("Unexpected XMLStream event at Document level: ENTITY_REFERENCE"); - case CDATA: - throw new JDOMException("Unexpected XMLStream event at Document level: CDATA"); - case SPACE: - // Can happen when XMLInputFactory2.P_REPORT_PROLOG_WHITESPACE is set to true - document.addContent(factory.text(stream.getText())); - break; case CHARACTERS: final String badTxt = stream.getText(); if (!Verifier.isAllXMLWhitespace(badTxt)) { @@ -59,12 +50,8 @@ final class SafeStAXStreamBuilder { // otherwise ignore the chars. break; - case COMMENT: - case PROCESSING_INSTRUCTION: - break; - default: - throw new JDOMException("Unexpected XMLStream event " + state); + throw new JDOMException("Unexpected XMLStream event at Document level:" + state); } if (stream.hasNext()) { @@ -77,15 +64,13 @@ final class SafeStAXStreamBuilder { return document; } - static Element build(@NotNull XMLStreamReader stream, boolean isIgnoreBoundaryWhitespace, @Nullable StringInterner stringInterner) throws JDOMException, XMLStreamException { + static Element build(@NotNull XMLStreamReader stream, @SuppressWarnings("SameParameterValue") boolean isIgnoreBoundaryWhitespace, @NotNull SafeJdomFactory factory) throws JDOMException, XMLStreamException { int state = stream.getEventType(); if (START_DOCUMENT != state) { throw new JDOMException("JDOM requires that XMLStreamReaders are at their beginning when being processed"); } - JDOMFactory factory = stringInterner == null ? SafeStAXStreamBuilder.factory : new JdomInternFactory(stringInterner); - Element rootElement = null; while (state != END_DOCUMENT) { switch (state) { @@ -120,7 +105,7 @@ final class SafeStAXStreamBuilder { return rootElement; } - private static Element processElementFragment(@NotNull XMLStreamReader reader, boolean isIgnoreBoundaryWhitespace, @NotNull JDOMFactory factory) throws XMLStreamException, JDOMException { + private static Element processElementFragment(@NotNull XMLStreamReader reader, boolean isIgnoreBoundaryWhitespace, @NotNull SafeJdomFactory factory) throws XMLStreamException, JDOMException { if (reader.getEventType() != START_ELEMENT) { throw new JDOMException("JDOM requires that the XMLStreamReader is at the START_ELEMENT state when retrieving an Element Fragment."); } @@ -146,19 +131,16 @@ final class SafeStAXStreamBuilder { case SPACE: if (!isIgnoreBoundaryWhitespace) { - current.addContent(factory.text(reader.getText())); + current.addContent(factory.text(reader.getText(), current)); } case CHARACTERS: if (!isIgnoreBoundaryWhitespace || !reader.isWhiteSpace()) { - current.addContent(factory.text(reader.getText())); + current.addContent(factory.text(reader.getText(), current)); } break; case ENTITY_REFERENCE: - current.addContent(factory.entityRef(reader.getLocalName())); - break; - case COMMENT: case PROCESSING_INSTRUCTION: break; @@ -172,12 +154,12 @@ final class SafeStAXStreamBuilder { } @NotNull - private static Element processElement(@NotNull XMLStreamReader reader, @NotNull JDOMFactory factory) { + private static Element processElement(@NotNull XMLStreamReader reader, @NotNull SafeJdomFactory factory) { final Element element = factory.element(reader.getLocalName(), Namespace.getNamespace(reader.getPrefix(), reader.getNamespaceURI())); // Handle attributes for (int i = 0, len = reader.getAttributeCount(); i < len; i++) { - factory.setAttribute(element, factory.attribute( + element.setAttribute(factory.attribute( reader.getAttributeLocalName(i), reader.getAttributeValue(i), AttributeType.getAttributeType(reader.getAttributeType(i)), diff --git a/platform/util/src/com/intellij/util/containers/Interner.java b/platform/util/src/com/intellij/util/containers/Interner.java index 50a5755438c5..291b3b825dbf 100644 --- a/platform/util/src/com/intellij/util/containers/Interner.java +++ b/platform/util/src/com/intellij/util/containers/Interner.java @@ -1,23 +1,10 @@ -/* - * Copyright 2000-2015 JetBrains s.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-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.util.containers; import gnu.trove.TObjectHashingStrategy; import org.jetbrains.annotations.NotNull; +import java.util.Collection; import java.util.Set; /** @@ -34,6 +21,11 @@ public class Interner { public Interner() { mySet = new OpenTHashSet(); } + + public Interner(@NotNull Collection initialItems) { + mySet = new OpenTHashSet(initialItems); + } + public Interner(@NotNull TObjectHashingStrategy strategy) { mySet = new OpenTHashSet(strategy); }