Skip to content

Commit

Permalink
Some performance optimization for #73
Browse files Browse the repository at this point in the history
  • Loading branch information
dhuebner committed Jul 14, 2023
1 parent 12952c7 commit 106df9e
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ class YangContentProposalProvider extends IdeContentProposalProvider {
XpathExpression: {
val type = xpathResolver.getType(obj)
if (type instanceof NodeSetType) {
return type.nodes
return type.allNodes
} else {
if (obj instanceof XpathLocation) {
val p = findPathes(obj.target, context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.List;
import java.util.Optional;

import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.SemanticTokens;
import org.eclipse.xtext.ide.editor.syntaxcoloring.IHighlightedPositionAcceptor;
Expand All @@ -22,11 +23,14 @@ public SemanticTokens highlightedPositionsToSemanticTokens(final Document doc, f

@Override
public void addPosition(int offset, int length, String... ids) {
if (cancelIndicator.isCanceled())
throw new OperationCanceledException();

Position position = doc.getPosition(offset);
for (String styleId : ids) {
Optional<TokenType> yangStyle = Arrays.stream(TokenType.values())
.filter(e -> e.getYangStyle().equals(styleId)).findFirst();
if (yangStyle.isPresent()) {
Position position = doc.getPosition(offset);
tokens.add(new SemanticToken(position.getLine() + 1, position.getCharacter() + 1, length,
yangStyle.get().ordinal(), 0));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.typefox.yang.scoping.xpath
import java.util.List
import org.eclipse.xtend.lib.annotations.Data
import org.eclipse.xtext.resource.IEObjectDescription
import com.google.common.base.Supplier

class Types {
public static val ANY = new XpathType() {}
Expand All @@ -11,24 +12,29 @@ class Types {
public static val STRING = new PrimitiveType("string")
public static val NUMBER= new PrimitiveType("number")

def static NodeSetType nodeSet(IEObjectDescription nodes) {
if (nodes === null) {
def static NodeSetType nodeSet(IEObjectDescription node) {
if (node === null) {
return nodeSet(#[])
} else {
return nodeSet(#[nodes])
} else {
return nodeSet(#[node])
}
}

def static NodeSetType nodeSet(List<IEObjectDescription> nodes) {
new NodeSetType(nodes)
new NodeSetType(null, [|nodes])
}

def static NodeSetType nodeSet(IEObjectDescription firstNode, Supplier<List<IEObjectDescription>> nodesSuplier) {
new NodeSetType(firstNode, nodesSuplier)
}

def static union(XpathType type, XpathType type2) {
val nodes = newArrayList
if (type instanceof NodeSetType) {
nodes.addAll(type.nodes)
nodes.addAll(type.allNodes)
}
if (type2 instanceof NodeSetType) {
nodes.addAll(type2.nodes)
nodes.addAll(type2.allNodes)
}
return nodeSet(nodes)
}
Expand All @@ -38,8 +44,30 @@ class Types {
interface XpathType {}


@Data class NodeSetType implements XpathType {
List<IEObjectDescription> nodes
class NodeSetType implements XpathType {
Supplier<List<IEObjectDescription>> nodes

IEObjectDescription first

new(IEObjectDescription first, Supplier<List<IEObjectDescription>> supplier) {
this.first = first
this.nodes = supplier
}

def getSingleNode() {
return if (first !== null) {
first
} else
allNodes.head
}

def getAllNodes() {
return nodes.get
}

def isEmpty() {
return if(first !== null) false else allNodes.empty
}
}

@Data class PrimitiveType implements XpathType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ import org.eclipse.xtext.resource.IEObjectDescription
import org.eclipse.xtext.util.internal.EmfAdaptable
import org.eclipse.xtext.util.internal.Log
import org.eclipse.xtext.scoping.IScope
import com.google.common.base.Suppliers
import java.util.Collections

@Log
class XpathResolver {
Expand Down Expand Up @@ -85,17 +87,16 @@ class XpathResolver {
return type
}

def XpathType doResolve(XpathExpression expression, QualifiedName contextNode, IScopeContext context) {
def void doResolve(XpathExpression expression, QualifiedName contextNode, IScopeContext context) {
val element = context.schemaNodeScope.getSingleElement(contextNode)
val initialContext =
if (element === null) {
Types.nodeSet(emptyList)
Types.nodeSet(Collections.EMPTY_LIST)
} else {
val allDescriptions = context.schemaNodeScope.getElements(element.EObjectOrProxy).toList
if (allDescriptions.empty)
Types.nodeSet(element)
else
Types.nodeSet(allDescriptions)
val allDescriptions = Suppliers.memoize([|
context.schemaNodeScope.getElements(element.EObjectOrProxy).toList
])
Types.nodeSet(element, allDescriptions)
}
internalResolve(expression, initialContext, new Context(context.schemaNodeScope, context.moduleName, initialContext))
}
Expand Down Expand Up @@ -201,7 +202,7 @@ class XpathResolver {
if (f.name == 'deref') {
val type = internalResolve(e.args.head, contextType, ctx)
if (type instanceof NodeSetType) {
val desc = type.nodes.head
val desc = type.singleNode
if (desc !== null) {
if (desc.EObjectOrProxy instanceof Leaf) {
val l = desc.EObjectOrProxy as Leaf
Expand All @@ -213,7 +214,7 @@ class XpathResolver {
}
}
}
return Types.nodeSet(#[Linker.ROOT]).install(e)
return Types.nodeSet(Linker.ROOT).install(e)
}
for (arg : e.args) {
internalResolve(arg, contextType, ctx)
Expand Down Expand Up @@ -252,7 +253,7 @@ class XpathResolver {

protected def dispatch XpathType internalResolveStep(CurrentRef e, XpathType contextType, Context ctx) {
linker.link(e, YangPackage.Literals.CURRENT_REF__REF) [
contextType.EObjectDescription
contextType.EObjectDescription
]
return contextType
}
Expand Down Expand Up @@ -299,7 +300,7 @@ class XpathResolver {
ref.set(contextType)
return contextType.EObjectDescription
} else {
val descs = contextType.nodes.filter[qualifiedName.lastSegment == qualifiedName.lastSegment].toList
val descs = contextType.allNodes.filter[qualifiedName.lastSegment == qualifiedName.lastSegment].toList
if (!descs.isEmpty) {
val newType = Types.nodeSet(descs)
ref.set(newType)
Expand Down Expand Up @@ -347,7 +348,7 @@ class XpathResolver {
}
val ref = new AtomicReference<XpathType>()
linker.link(e.node, YangPackage.Literals.XPATH_NAME_TEST__REF) [
val type = computeType(contextType, resolveModulePrefix(e), mode, ctx)
val type = computeSingleType(contextType, resolveModulePrefix(e), mode, ctx)
ref.set(type)
return type.EObjectDescription
]
Expand Down Expand Up @@ -379,23 +380,35 @@ class XpathResolver {
DESCENDANTS_OR_SELF
}

protected def XpathType computeSingleType(XpathType type, QualifiedName name, Axis mode, Context ctx) {
computeType(type, name, mode, true, ctx)
}

protected def XpathType computeType(XpathType type, QualifiedName name, Axis mode, Context ctx) {
computeType(type, name, mode, false, ctx)
}

private def XpathType computeType(XpathType type, QualifiedName name, Axis mode, boolean onlyFirst, Context ctx) {
if (type instanceof NodeSetType) {
// handle root
if (type.nodes.empty) {
if (type.isEmpty) {
val nodes = findNodes(QualifiedName.EMPTY, name, mode, ctx.nodeScope)
if ( nodes.empty) {
if (nodes.empty) {
return Types.ANY
}
return Types.nodeSet(nodes)
}
val result = newLinkedHashSet()
for (n : type.nodes) {
val nodes = findNodes(n.qualifiedName, name, mode, ctx.nodeScope)
result.addAll(nodes)
}
if (!result.empty) {
return Types.nodeSet(result.toList)
if (onlyFirst) {
return Types.nodeSet(findNodes(type.singleNode.qualifiedName, name, mode, ctx.nodeScope))
} else {
for (n : type.allNodes) {
val nodes = findNodes(n.qualifiedName, name, mode, ctx.nodeScope)
result.addAll(nodes)
}
if (!result.empty) {
return Types.nodeSet(result.toList)
}
}
}
return Types.ANY
Expand All @@ -410,34 +423,37 @@ class XpathResolver {
}

def List<IEObjectDescription> findNodes(QualifiedName prefix, QualifiedName name, Axis mode, IScope nodeScope) {
val prefixWithoutLast = prefix.skipLast
if (mode === Axis.SIBLINGS) {
return findNodes(prefix.skipLast, name, Axis.CHILDREN, nodeScope)
return findNodes(prefixWithoutLast, name, Axis.CHILDREN, nodeScope)
} else if (mode === Axis.DESCENDANTS_OR_SELF) {
return findNodes(prefix.skipLast, name, Axis.DESCENDANTS, nodeScope)
return findNodes(prefixWithoutLast, name, Axis.DESCENDANTS, nodeScope)
} else if (mode == Axis.ANCESTOR) {
return findNodes(prefix.skipLast, name, Axis.ANCESTOR_OR_SELF, nodeScope)
return findNodes(prefixWithoutLast, name, Axis.ANCESTOR_OR_SELF, nodeScope)
} else if (mode == Axis.ANCESTOR_OR_SELF) {
return getNodeHierarchy(prefix, nodeScope, false)
.filter[name === null || name == ASTERISK || qualifiedName.endsWith(name)]
.toList
} else if (mode == Axis.PARENT) {
val parent = getNodeHierarchy(prefix.skipLast, nodeScope, true).head
val parent = getNodeHierarchy(prefixWithoutLast, nodeScope, true).head
if (parent !== null)
return #[parent]
else
return #[Linker.ROOT]
} else {
val elements = nodeScope.allElements.filter [
if (qualifiedName.startsWith(prefix) && qualifiedName.segmentCount > prefix.segmentCount) {
if (name === null || name == ASTERISK || qualifiedName.endsWith(name)) {
if (mode === Axis.DESCENDANTS) {
return true
} else {
// Check whether all intermediate nodes are schema-only nodes
val parents = getNodeHierarchy(qualifiedName.skipLast, nodeScope, false)
.filter[qualifiedName.segmentCount > prefix.segmentCount]
return parents.isEmpty
}
val elements = nodeScope.allElements.filter [descr |
val qName = descr.qualifiedName
if (qName.segmentCount > prefix.segmentCount
&& (name === null || name == ASTERISK || qName.endsWith(name))
&& startsWith(qName, prefix)
) {
if (mode === Axis.DESCENDANTS) {
return true
} else {
// Check whether all intermediate nodes are schema-only nodes
val parents = getNodeHierarchy(qName.skipLast, nodeScope, false)
.filter[qualifiedName.segmentCount > prefix.segmentCount]
return parents.isEmpty
}
}
return false
Expand All @@ -448,8 +464,31 @@ class XpathResolver {

private def endsWith(QualifiedName parent, QualifiedName child) {
val offset = parent.segmentCount - child.segmentCount
if (offset >= 0)
if (offset >= 0) {
// called very often: skipFirst does an array copy and creates new QName obj
// in case there is only one segment, compare the last only
if(child.segmentCount == 1) {
return parent.lastSegment == child.lastSegment
}
return parent.skipFirst(offset) == child
}
else
return false
}

private def startsWith(QualifiedName parent, QualifiedName prefix) {
// called very often. Implemented `startsWith` with some optimizations
val offset = parent.segmentCount - prefix.segmentCount
if (offset >= 0) {
if(prefix.segmentCount == 1) {
return parent.firstSegment == prefix.firstSegment
}
for (var i = prefix.getSegmentCount() - 1; i >= 0; i--) {
if(!parent.getSegment(i).equals(prefix.getSegment(i)))
return false;
}
return true;
}
else
return false
}
Expand All @@ -462,18 +501,22 @@ class XpathResolver {
QualifiedName startQName, IScope nodeScope, boolean stopAtNull) {
[
new AbstractIterator<IEObjectDescription> {

QualifiedName qname = startQName
IEObjectDescription descr

override protected computeNext() {
while (qname.segmentCount >= 2) {
// Look up node description in scope, if not already done before (see below)
val nextDescr = descr ?: nodeScope.getSingleElement(qname)
qname = qname.skipLast
descr = null
if (nextDescr === null && stopAtNull)
return endOfData

qname = qname.skipLast
descr = null

var isInstance = nextDescr.isInstanceNode
if (isInstance && nextDescr.EObjectOrProxy instanceof SchemaNode && qname.segmentCount >= 2) {
if (isInstance && qname.segmentCount >= 2 && nextDescr.EObjectOrProxy instanceof SchemaNode) {
// If the direct parent of a SchemaNode is a Choice, an implicit Case with the same name is inserted.
// In this case we skip the implicit Case (and the containing Choice).
descr = nodeScope.getSingleElement(qname)
Expand All @@ -498,7 +541,7 @@ class XpathResolver {

protected def getEObjectDescription(XpathType type) {
if (type instanceof NodeSetType) {
return type.nodes.head
return type.singleNode
}
return null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class JsonFileBasedPreferenceValues extends MapBasedPreferenceValues {
} else {
entry.key
}
this.put(key, v.asString.toString)
this.put(key, v.asString.toString)
}
}
}
Expand Down

0 comments on commit 106df9e

Please sign in to comment.