mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
speed up macro collapse — 200ms down to 90ms
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
// Copyright 2000-2017 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.
|
||||
// Copyright 2000-2018 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.application;
|
||||
|
||||
import org.jdom.Attribute;
|
||||
import org.jdom.Element;
|
||||
import org.jdom.Text;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -16,18 +15,10 @@ public abstract class PathMacroFilter {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean skipPathMacros(Text element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean skipPathMacros(@NotNull Attribute attribute) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean recursePathMacros(Text element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean recursePathMacros(Attribute attribute) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* 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-2018 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.components;
|
||||
|
||||
import com.intellij.openapi.application.PathMacroFilter;
|
||||
@@ -40,26 +26,22 @@ public abstract class PathMacroMap {
|
||||
substitute(e, caseSensitive, false);
|
||||
}
|
||||
|
||||
public final void substitute(@NotNull Element e, boolean caseSensitive, boolean recursively, @Nullable PathMacroFilter filter) {
|
||||
if (filter != null && filter.skipPathMacros(e)) {
|
||||
public final void substitute(@NotNull Element element, boolean caseSensitive, boolean recursively, @Nullable PathMacroFilter filter) {
|
||||
if (filter != null && filter.skipPathMacros(element)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Content child : e.getContent()) {
|
||||
for (Content child : element.getContent()) {
|
||||
if (child instanceof Element) {
|
||||
substitute((Element)child, caseSensitive, recursively, filter);
|
||||
}
|
||||
else if (child instanceof Text) {
|
||||
Text t = (Text)child;
|
||||
if (filter == null || !filter.skipPathMacros(t)) {
|
||||
String oldText = t.getText();
|
||||
String newText = (recursively || (filter != null && filter.recursePathMacros(t)))
|
||||
? substituteRecursively(oldText, caseSensitive)
|
||||
: substitute(oldText, caseSensitive);
|
||||
if (oldText != newText) {
|
||||
// it is faster to call 'setText' right away than perform additional 'equals' check
|
||||
t.setText(newText);
|
||||
}
|
||||
String oldText = t.getText();
|
||||
String newText = recursively ? substituteRecursively(oldText, caseSensitive) : substitute(oldText, caseSensitive);
|
||||
if (oldText != newText) {
|
||||
// it is faster to call 'setText' right away than perform additional 'equals' check
|
||||
t.setText(newText);
|
||||
}
|
||||
}
|
||||
else if (!(child instanceof Comment)) {
|
||||
@@ -67,13 +49,14 @@ public abstract class PathMacroMap {
|
||||
}
|
||||
}
|
||||
|
||||
for (Attribute attribute : e.getAttributes()) {
|
||||
if (!element.hasAttributes()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Attribute attribute : element.getAttributes()) {
|
||||
if (filter == null || !filter.skipPathMacros(attribute)) {
|
||||
String oldValue = attribute.getValue();
|
||||
String newValue = (recursively || (filter != null && filter.recursePathMacros(attribute)))
|
||||
? substituteRecursively(oldValue, caseSensitive)
|
||||
: substitute(oldValue, caseSensitive);
|
||||
if (oldValue != newValue) {
|
||||
String newValue = getAttributeValue(attribute, filter, caseSensitive, recursively);
|
||||
if (attribute.getValue() != newValue) {
|
||||
// it is faster to call 'setValue' right away than perform additional 'equals' check
|
||||
attribute.setValue(newValue);
|
||||
}
|
||||
@@ -81,6 +64,16 @@ public abstract class PathMacroMap {
|
||||
}
|
||||
}
|
||||
|
||||
public String getAttributeValue(@NotNull Attribute attribute, @Nullable PathMacroFilter filter, boolean caseSensitive, boolean recursively) {
|
||||
String oldValue = attribute.getValue();
|
||||
if (recursively || (filter != null && filter.recursePathMacros(attribute))) {
|
||||
return substituteRecursively(oldValue, caseSensitive);
|
||||
}
|
||||
else {
|
||||
return substitute(oldValue, caseSensitive);
|
||||
}
|
||||
}
|
||||
|
||||
public final void substitute(@NotNull Element e, boolean caseSensitive, final boolean recursively) {
|
||||
substitute(e, caseSensitive, recursively, null);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,10 @@
|
||||
*/
|
||||
package com.intellij.configurationStore;
|
||||
|
||||
import com.intellij.application.options.ReplacePathToMacroMap;
|
||||
import com.intellij.openapi.application.PathMacroFilter;
|
||||
import com.intellij.openapi.util.JDOMUtil;
|
||||
import com.intellij.openapi.util.SystemInfoRt;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import org.jdom.*;
|
||||
import org.jdom.output.Format;
|
||||
@@ -65,7 +68,7 @@ import java.io.Writer;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("Duplicates")
|
||||
class JbXmlOutputter {
|
||||
final class JbXmlOutputter {
|
||||
private static final Format DEFAULT_FORMAT = JDOMUtil.createFormat("\n");
|
||||
|
||||
// For normal output
|
||||
@@ -73,9 +76,19 @@ class JbXmlOutputter {
|
||||
@Nullable
|
||||
private final JDOMUtil.ElementOutputFilter elementFilter;
|
||||
|
||||
public JbXmlOutputter(@NotNull String lineSeparator, @Nullable JDOMUtil.ElementOutputFilter elementFilter) {
|
||||
@Nullable
|
||||
private final ReplacePathToMacroMap macroMap;
|
||||
@Nullable
|
||||
private final PathMacroFilter macroFilter;
|
||||
|
||||
public JbXmlOutputter(@NotNull String lineSeparator,
|
||||
@Nullable JDOMUtil.ElementOutputFilter elementFilter,
|
||||
@Nullable ReplacePathToMacroMap macroMap,
|
||||
@Nullable PathMacroFilter macroFilter) {
|
||||
this.format = DEFAULT_FORMAT.getLineSeparator().equals(lineSeparator) ? DEFAULT_FORMAT : JDOMUtil.createFormat(lineSeparator);
|
||||
this.elementFilter = elementFilter;
|
||||
this.macroMap = macroMap;
|
||||
this.macroFilter = macroFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,7 +182,7 @@ class JbXmlOutputter {
|
||||
* @param out <code>Writer</code> to use.
|
||||
* @param encoding The encoding to add to the declaration
|
||||
*/
|
||||
protected void printDeclaration(Writer out, String encoding) throws IOException {
|
||||
private void printDeclaration(Writer out, String encoding) throws IOException {
|
||||
// Only print the declaration if it's not being omitted
|
||||
if (!format.getOmitDeclaration()) {
|
||||
// Assume 1.0 version
|
||||
@@ -192,7 +205,7 @@ class JbXmlOutputter {
|
||||
* @param docType <code>Document</code> whose declaration to write.
|
||||
* @param out <code>Writer</code> to use.
|
||||
*/
|
||||
protected void printDocType(Writer out, DocType docType) throws IOException {
|
||||
private void printDocType(Writer out, DocType docType) throws IOException {
|
||||
|
||||
String publicID = docType.getPublicID();
|
||||
String systemID = docType.getSystemID();
|
||||
@@ -230,7 +243,7 @@ class JbXmlOutputter {
|
||||
* @param comment <code>Comment</code> to write.
|
||||
* @param out <code>Writer</code> to use.
|
||||
*/
|
||||
protected void printComment(Writer out, Comment comment)
|
||||
private static void printComment(Writer out, Comment comment)
|
||||
throws IOException {
|
||||
out.write("<!--");
|
||||
out.write(comment.getText());
|
||||
@@ -243,7 +256,7 @@ class JbXmlOutputter {
|
||||
* @param pi <code>ProcessingInstruction</code> to write.
|
||||
* @param out <code>Writer</code> to use.
|
||||
*/
|
||||
protected void printProcessingInstruction(Writer out, ProcessingInstruction pi) throws IOException {
|
||||
private void printProcessingInstruction(Writer out, ProcessingInstruction pi) throws IOException {
|
||||
String target = pi.getTarget();
|
||||
boolean piProcessed = false;
|
||||
|
||||
@@ -283,7 +296,7 @@ class JbXmlOutputter {
|
||||
* @param entity <code>EntityRef</code> to output.
|
||||
* @param out <code>Writer</code> to use.
|
||||
*/
|
||||
protected void printEntityRef(Writer out, EntityRef entity)
|
||||
private static void printEntityRef(Writer out, EntityRef entity)
|
||||
throws IOException {
|
||||
out.write("&");
|
||||
out.write(entity.getName());
|
||||
@@ -296,7 +309,7 @@ class JbXmlOutputter {
|
||||
* @param cdata <code>CDATA</code> to output.
|
||||
* @param out <code>Writer</code> to use.
|
||||
*/
|
||||
protected void printCDATA(Writer out, CDATA cdata) throws IOException {
|
||||
private void printCDATA(Writer out, CDATA cdata) throws IOException {
|
||||
String str;
|
||||
if (format.getTextMode() == Format.TextMode.NORMALIZE) {
|
||||
str = cdata.getTextNormalize();
|
||||
@@ -320,6 +333,11 @@ class JbXmlOutputter {
|
||||
else if (format.getTextMode() == Format.TextMode.TRIM) {
|
||||
str = str.trim();
|
||||
}
|
||||
|
||||
if (macroMap != null) {
|
||||
str = macroMap.substitute(str, SystemInfoRt.isFileSystemCaseSensitive);
|
||||
}
|
||||
|
||||
out.write(escapeElementEntities(str));
|
||||
}
|
||||
|
||||
@@ -328,11 +346,11 @@ class JbXmlOutputter {
|
||||
* its <code>{@link Attribute}</code>s, and all contained (child)
|
||||
* elements, etc.
|
||||
*
|
||||
* @param element <code>Element</code> to output.
|
||||
* @param out <code>Writer</code> to use.
|
||||
* @param level <code>int</code> level of indention.
|
||||
* @param element <code>Element</code> to output.
|
||||
* @param out <code>Writer</code> to use.
|
||||
* @param level <code>int</code> level of indention.
|
||||
*/
|
||||
protected void printElement(Writer out, Element element, int level) throws IOException {
|
||||
public void printElement(Writer out, Element element, int level) throws IOException {
|
||||
if (elementFilter != null && !elementFilter.accept(element, level)) {
|
||||
return;
|
||||
}
|
||||
@@ -366,17 +384,16 @@ class JbXmlOutputter {
|
||||
else {
|
||||
out.write('>');
|
||||
|
||||
// For a special case where the content is only CDATA
|
||||
// or Text we don't want to indent after the start or before the end tag.
|
||||
// for a special case where the content is only CDATA or Text we don't want to indent after the start or before the end tag
|
||||
if (nextNonText(content, start) < size) {
|
||||
// Case Mixed Content - normal indentation
|
||||
// case Mixed Content - normal indentation
|
||||
newline(out);
|
||||
printContentRange(out, content, start, size, level + 1);
|
||||
newline(out);
|
||||
indent(out, level);
|
||||
}
|
||||
else {
|
||||
// Case all CDATA or Text - no indentation
|
||||
// case all CDATA or Text - no indentation
|
||||
printTextRange(out, content, start, size);
|
||||
}
|
||||
out.write("</");
|
||||
@@ -391,11 +408,11 @@ class JbXmlOutputter {
|
||||
* starting index is inclusive, while the ending index is
|
||||
* exclusive.
|
||||
*
|
||||
* @param content <code>List</code> of content to output
|
||||
* @param start index of first content node (inclusive.
|
||||
* @param end index of last content node (exclusive).
|
||||
* @param out <code>Writer</code> to use.
|
||||
* @param level <code>int</code> level of indentation.
|
||||
* @param content <code>List</code> of content to output
|
||||
* @param start index of first content node (inclusive.
|
||||
* @param end index of last content node (exclusive).
|
||||
* @param out <code>Writer</code> to use.
|
||||
* @param level <code>int</code> level of indentation.
|
||||
*/
|
||||
private void printContentRange(Writer out, List<Content> content, int start, int end, int level) throws IOException {
|
||||
boolean firstNode; // Flag for 1st node in content
|
||||
@@ -441,13 +458,11 @@ class JbXmlOutputter {
|
||||
printProcessingInstruction(out, (ProcessingInstruction)next);
|
||||
}
|
||||
else {
|
||||
// XXX if we get here then we have a illegal content, for
|
||||
// now we'll just ignore it (probably should throw
|
||||
// a exception)
|
||||
// XXX if we get here then we have a illegal content, for now we'll just ignore it (probably should throw a exception)
|
||||
}
|
||||
|
||||
index++;
|
||||
} /* while */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -467,18 +482,18 @@ class JbXmlOutputter {
|
||||
|
||||
previous = null;
|
||||
|
||||
// Remove leading whitespace-only nodes
|
||||
// remove leading whitespace-only nodes
|
||||
start = skipLeadingWhite(content, start);
|
||||
|
||||
int size = content.size();
|
||||
if (start < size) {
|
||||
// And remove trialing whitespace-only nodes
|
||||
// and remove trialing whitespace-only nodes
|
||||
end = skipTrailingWhite(content, end);
|
||||
|
||||
for (int i = start; i < end; i++) {
|
||||
node = content.get(i);
|
||||
|
||||
// Get the unmangled version of the text we are about to print
|
||||
// get the unmangled version of the text we are about to print
|
||||
if (node instanceof Text) {
|
||||
next = ((Text)node).getText();
|
||||
}
|
||||
@@ -489,13 +504,12 @@ class JbXmlOutputter {
|
||||
throw new IllegalStateException("Should see only CDATA, Text, or EntityRef");
|
||||
}
|
||||
|
||||
// This may save a little time
|
||||
// this may save a little time
|
||||
if (next == null || next.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine if we need to pad the output (padding is
|
||||
// only need in trim or normalizing mode)
|
||||
// determine if we need to pad the output (padding is only need in trim or normalizing mode)
|
||||
if (previous != null) { // Not 1st node
|
||||
if (format.getTextMode() == Format.TextMode.NORMALIZE || format.getTextMode() == Format.TextMode.TRIM) {
|
||||
if (endsWithWhite(previous) || startsWithWhite(next)) {
|
||||
@@ -526,13 +540,22 @@ class JbXmlOutputter {
|
||||
* @param attributes <code>List</code> of Attribute objects
|
||||
* @param out <code>Writer</code> to use
|
||||
*/
|
||||
protected void printAttributes(Writer out, List<Attribute> attributes) throws IOException {
|
||||
private void printAttributes(Writer out, List<Attribute> attributes) throws IOException {
|
||||
for (Attribute attribute : attributes) {
|
||||
out.write(' ');
|
||||
printQualifiedName(out, attribute);
|
||||
out.write('=');
|
||||
out.write('"');
|
||||
out.write(escapeAttributeEntities(attribute.getValue()));
|
||||
|
||||
String value;
|
||||
if (macroMap != null && (macroFilter == null || !macroFilter.skipPathMacros(attribute))) {
|
||||
value = macroMap.getAttributeValue(attribute, macroFilter, SystemInfoRt.isFileSystemCaseSensitive, false);
|
||||
}
|
||||
else {
|
||||
value = attribute.getValue();
|
||||
}
|
||||
|
||||
out.write(escapeAttributeEntities(value));
|
||||
out.write('"');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.intellij.openapi.components.impl.stores.FileStorageCoreUtil
|
||||
import com.intellij.openapi.diagnostic.debug
|
||||
import com.intellij.openapi.diagnostic.runAndLogException
|
||||
import com.intellij.openapi.util.JDOMUtil
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.openapi.util.SystemInfoRt
|
||||
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream
|
||||
import com.intellij.openapi.vfs.safeOutputStream
|
||||
import com.intellij.util.LineSeparator
|
||||
@@ -230,7 +230,7 @@ private class XmlDataWriter(private val rootElementName: String?,
|
||||
writer.append(entry.key)
|
||||
writer.append('=')
|
||||
writer.append('"')
|
||||
writer.append(replacePathMap?.substitute(JDOMUtil.escapeText(entry.value, false, true), SystemInfo.isFileSystemCaseSensitive))
|
||||
writer.append(replacePathMap?.substitute(JDOMUtil.escapeText(entry.value, false, true), SystemInfoRt.isFileSystemCaseSensitive))
|
||||
writer.append('"')
|
||||
}
|
||||
|
||||
@@ -243,13 +243,12 @@ private class XmlDataWriter(private val rootElementName: String?,
|
||||
writer.append('>')
|
||||
}
|
||||
|
||||
val xmlOutputter = JbXmlOutputter(lineSeparatorWithIndent, filter?.toElementFilter())
|
||||
val xmlOutputter = JbXmlOutputter(lineSeparatorWithIndent, filter?.toElementFilter(), replacePathMap, macroFilter)
|
||||
for (element in elements) {
|
||||
if (hasRootElement) {
|
||||
writer.append(lineSeparatorWithIndent)
|
||||
}
|
||||
|
||||
replacePathMap?.substitute(element, SystemInfo.isFileSystemCaseSensitive, false, macroFilter)
|
||||
xmlOutputter.printElement(writer, element, 0)
|
||||
}
|
||||
|
||||
@@ -414,7 +413,7 @@ internal fun createDataWriterForElement(element: Element): DataWriter {
|
||||
override fun hasData(filter: DataWriterFilter) = filter.hasData(element)
|
||||
|
||||
override fun write(output: OutputStream, lineSeparator: String, filter: DataWriterFilter?) {
|
||||
output.bufferedWriter().use { JbXmlOutputter(lineSeparator, filter?.toElementFilter()).output(element, it) }
|
||||
output.bufferedWriter().use { JbXmlOutputter(lineSeparator, filter?.toElementFilter(), null, null).output(element, it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-5
@@ -5,6 +5,7 @@ import com.intellij.openapi.components.PathMacroMap;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.util.text.StringUtilRt;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.ContainerUtilRt;
|
||||
@@ -79,9 +80,9 @@ public class ReplacePathToMacroMap extends PathMacroMap {
|
||||
return text;
|
||||
}
|
||||
|
||||
boolean startsWith = caseSensitive ? text.startsWith(path) : StringUtil.startsWithIgnoreCase(text, path);
|
||||
|
||||
if (!startsWith) return text;
|
||||
if (!(caseSensitive ? text.startsWith(path) : StringUtilRt.startsWithIgnoreCase(text, path))) {
|
||||
return text;
|
||||
}
|
||||
|
||||
//check that this is complete path (ends with "/" or "!/")
|
||||
// do not collapse partial paths, i.e. do not substitute "/a/b/cd" in paths like "/a/b/cdeFgh"
|
||||
@@ -90,11 +91,17 @@ public class ReplacePathToMacroMap extends PathMacroMap {
|
||||
if (!isWindowsRoot &&
|
||||
endOfOccurrence < text.length() &&
|
||||
text.charAt(endOfOccurrence) != '/' &&
|
||||
!text.substring(endOfOccurrence).startsWith("!/")) {
|
||||
!(text.charAt(endOfOccurrence) == '!' && text.substring(endOfOccurrence).startsWith("!/"))) {
|
||||
return text;
|
||||
}
|
||||
|
||||
return myMacroMap.get(path) + text.substring(endOfOccurrence);
|
||||
String s = myMacroMap.get(path);
|
||||
if (text.length() > endOfOccurrence) {
|
||||
return s + text.substring(endOfOccurrence);
|
||||
}
|
||||
else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+1
-18
@@ -1,10 +1,9 @@
|
||||
// Copyright 2000-2017 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.
|
||||
// Copyright 2000-2018 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.components;
|
||||
|
||||
import com.intellij.openapi.application.PathMacroFilter;
|
||||
import org.jdom.Attribute;
|
||||
import org.jdom.Element;
|
||||
import org.jdom.Text;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -27,14 +26,6 @@ public class CompositePathMacroFilter extends PathMacroFilter {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean skipPathMacros(Text element) {
|
||||
for (PathMacroFilter filter : myFilters) {
|
||||
if (filter.skipPathMacros(element)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean skipPathMacros(@NotNull Attribute attribute) {
|
||||
for (PathMacroFilter filter : myFilters) {
|
||||
@@ -45,14 +36,6 @@ public class CompositePathMacroFilter extends PathMacroFilter {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean recursePathMacros(Text element) {
|
||||
for (PathMacroFilter filter : myFilters) {
|
||||
if (filter.recursePathMacros(element)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean recursePathMacros(Attribute attribute) {
|
||||
for (PathMacroFilter filter : myFilters) {
|
||||
|
||||
Reference in New Issue
Block a user