package org.exist.storage.statistics; import org.exist.Namespaces; import org.exist.dom.QName; import org.exist.dom.SymbolTable; import org.exist.storage.NodePath; import org.w3c.dom.Node; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; import java.nio.ByteBuffer; import java.util.List; /** * Collects statistics for a single node in the data guide. */ class NodeStats { private QName qname; private int nodeCount = 0; private int maxDepth = 0; transient private int depth = 0; protected NodeStats parent = null; protected NodeStats[] children = null; protected NodeStats(QName qname) { this(null, qname); } protected NodeStats(NodeStats parent, QName qname) { this.parent = parent; this.qname = qname; } public void incDepth() { this.depth++; } public void updateMaxDepth() { if (depth > maxDepth) maxDepth = depth; depth = 0; } public int getMaxDepth() { return maxDepth; } protected void addOccurrence() { nodeCount++; } protected NodeStats addChild(QName qn) { if (children != null) { for (int i = 0; i < children.length; i++) { NodeStats child = children[i]; if (child.qname.equalsSimple(qn)) { return child; } } } if (children == null) { children = new NodeStats[1]; } else { NodeStats[] tc = new NodeStats[children.length + 1]; System.arraycopy(children, 0, tc, 0, children.length); children = tc; } children[children.length - 1] = new NodeStats(this, qn); return children[children.length - 1]; } protected void mergeInto(DataGuide other, NodePath currentPath) { NodePath newPath; if (qname == null) newPath = currentPath; else { newPath = new NodePath(currentPath); newPath.addComponent(qname); other.add(newPath, this); } if (children != null) { for (int i = 0; i < children.length; i++) { NodeStats child = children[i]; child.mergeInto(other, newPath); } } } protected void mergeStats(NodeStats other) { nodeCount += other.nodeCount; if (other.maxDepth > maxDepth) maxDepth = other.maxDepth; } protected int getSize() { int s = qname == null ? 0 : 1; if (children != null) { for (int i = 0; i < children.length; i++) { s += children[i].getSize(); } } return s; } protected void getMaxParentDepth(QName name, NodeStats max) { if (parent != null && qname != null && qname.equalsSimple(name)) { max.maxDepth = Math.max(parent.maxDepth, max.maxDepth); } if (children != null) { for (int i = 0; i < children.length; i++) { children[i].getMaxParentDepth(name, max); } } } protected void write(ByteBuffer buffer, SymbolTable symbols) { buffer.putShort(symbols.getNSSymbol(qname.getNamespaceURI())); buffer.putShort(symbols.getSymbol(qname.getLocalName())); buffer.putInt(nodeCount); buffer.putInt(maxDepth); buffer.putInt(children == null ? 0: children.length); if (children != null) { for (int i = 0; i < children.length; i++) { children[i].write(buffer, symbols); } } } protected void read(ByteBuffer buffer, SymbolTable symbols) { short nsid = buffer.getShort(); short localid = buffer.getShort(); String namespaceURI = symbols.getNamespace(nsid); String localName = symbols.getName(localid); qname = symbols.getQName(Node.ELEMENT_NODE, namespaceURI, localName, ""); nodeCount = buffer.getInt(); maxDepth = buffer.getInt(); int childCount = buffer.getInt(); if (childCount > 0) { children = new NodeStats[childCount]; for (int i = 0; i < childCount; i++) { children[i] = new NodeStats(this, null); children[i].read(buffer, symbols); } } } protected void dump(StringBuilder currentPath, List paths) { StringBuilder newPath; if (qname == null) newPath = currentPath; else { newPath = new StringBuilder(currentPath); if (newPath.length() > 0) newPath.append(" -> "); newPath.append(qname); newPath.append('[').append(nodeCount).append(','); newPath.append(maxDepth).append(']'); } paths.add(newPath); if (children != null) { for (int i = 0; i < children.length; i++) { NodeStats child = children[i]; child.dump(newPath, paths); } } } public void toSAX(ContentHandler handler) throws SAXException { AttributesImpl attribs = new AttributesImpl(); attribs.addAttribute("", "name", "name", "CDATA", qname.getLocalName()); attribs.addAttribute("", "namespace", "namespace", "CDATA", qname.getNamespaceURI()); attribs.addAttribute("", "node-count", "node-count", "CDATA", Integer.toString(nodeCount)); attribs.addAttribute("", "max-depth", "max-depth", "CDATA", Integer.toString(maxDepth)); handler.startElement(Namespaces.EXIST_NS, "node", "node", attribs); if (children != null) { for (int i = 0; i < children.length; i++) { children[i].toSAX(handler); } } handler.endElement(Namespaces.EXIST_NS, "node", "node"); } }