/* * Sun Public License * * The contents of this file are subject to the Sun Public License Version * 1.0 (the "License"). You may not use this file except in compliance with * the License. A copy of the License is available at http://www.sun.com/ * * The Original Code is the SLAMD Distributed Load Generation Engine. * The Initial Developer of the Original Code is Neil A. Wilson. * Portions created by Neil A. Wilson are Copyright (C) 2004-2010. * Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): Neil A. Wilson */ package com.slamd.tools.ldifstructure; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.TreeMap; /** * This class defines a data structure that holds information about a node in * the directory tree. In particular, it includes the DN of the entry * associated with this node, a reference to the node for the parent entry, a * count of all immediate subordinate entries, and a list of all the child nodes * that themselves have one or more subordinate entries. * * * @author Neil A. Wilson */ public class LDIFNode { // The list of child nodes for this node. private ArrayList<LDIFNode> childNodes; // The number of immediate children for this node. private int numChildren; // The reference to the node for the parent entry. private LDIFNode parentNode; // The normalized DN for this entry. private String normalizedDN; // The mapping used to keep track of the entry types of the direct // subordinates. private TreeMap<String,LDIFEntryType> childEntryTypes; /** * Creates a new LDIF node with the provided information. * * @param normalizedDN The normalized DN of the entry associated with this * LDIF node. * @param parentNode The reference to the parent node. It may be * <CODE>null</CODE> if this node does not have a * parent. */ public LDIFNode(String normalizedDN, LDIFNode parentNode) { this.normalizedDN = normalizedDN; this.parentNode = parentNode; numChildren = 0; childNodes = new ArrayList<LDIFNode>(); childEntryTypes = new TreeMap<String,LDIFEntryType>(); } /** * Retrieves the normalized DN for this LDIF node. * * @return The normalized DN for this LDIF node. */ public String getNormalizedDN() { return normalizedDN; } /** * Retrieves the reference to the parent node for this LDIF node. * * @return The reference to the parent node for this LDIF node, or * <CODE>null</CODE> if this node does not have a parent. */ public LDIFNode getParentNode() { return parentNode; } /** * Retrieves the number of entries that are immediate children of this node. * * @return The number of entries that are immediate children of this node. */ public int getNumChildren() { return numChildren; } /** * Retrieves the number of entries that are direct or indirect descendants of * this entry. * * @return The number of entries that are descendants of this entry. */ public int getNumDescendants() { int descendantCount = numChildren; for (int i=0; i < childNodes.size(); i++) { descendantCount += childNodes.get(i).getNumDescendants(); } return descendantCount; } /** * Retrieves the set of entry types for the child entries. * * @return The set of entry types for the child entries. */ public TreeMap getChildEntryTypes() { return childEntryTypes; } /** * Retrieves the set of entry types for the child entries, sorted in * descending order of the number of entries of that type. * * @return The set of entry types for the child entries, sorted in descending * order of the number of entries of that type. */ public LDIFEntryType[] getSortedChildTypes() { LDIFEntryType[] childTypes = new LDIFEntryType[childEntryTypes.values().size()]; childEntryTypes.values().toArray(childTypes); for (int i=0; i < childTypes.length; i++) { int mostMatches = childTypes[i].getNumEntries(); for (int j=i+1; j < childTypes.length; j++) { int currentMatches = childTypes[j].getNumEntries(); if (currentMatches > mostMatches) { LDIFEntryType t = childTypes[i]; childTypes[i] = childTypes[j]; childTypes[j] = t; mostMatches = currentMatches; } } } return childTypes; } /** * Retrieves an entry type definition that is constructed from all the child * entry types associated with this node. * * @return An entry type definition that is constructed from all the child * entry types associated with this node, or <CODE>null</CODE> if * there are no child entry types. */ public LDIFEntryType getAggregateChildEntryType() { if (childEntryTypes.isEmpty()) { return null; } LDIFEntryType aggregateType = new LDIFEntryType(); Iterator iterator = childEntryTypes.values().iterator(); while (iterator.hasNext()) { aggregateType.aggregate((LDIFEntryType) iterator.next()); } return aggregateType; } /** * Increments the total number of entries that are immediate children of this * node. * * @param childEntry The entry to add as the child of this node. */ public void addChild(LDIFEntry childEntry) { numChildren++; LDIFAttribute attr = childEntry.getAttribute("objectclass"); if (attr == null) { return; } ArrayList values = attr.getValues(); String[] objectClasses = new String[values.size()]; if (objectClasses.length == 0) { return; } for (int i=0; i < objectClasses.length; i++) { objectClasses[i] = LDIFReader.toLowerCase((String) values.get(i)); } Arrays.sort(objectClasses); StringBuilder keyStr = new StringBuilder(); keyStr.append(objectClasses[0]); for (int i=1; i < objectClasses.length; i++) { keyStr.append(' '); keyStr.append(objectClasses[i]); } String key = keyStr.toString(); LDIFEntryType entryType = childEntryTypes.get(key); if (entryType == null) { entryType = new LDIFEntryType(childEntry, objectClasses); childEntryTypes.put(key, entryType); } else { entryType.update(childEntry); } } /** * Retrives the set of child nodes for this LDIF node. There should be a * child node for each immediate child entry that itself has one or more * children. * * @return The set of child nodes for this LDIF node. */ public ArrayList getChildNodes() { return childNodes; } /** * Adds the provided node to the set of child nodes for this LDIF node. * * @param childNode The node to add to the set of child nodes for this LDIF * node. */ public void addChildNode(LDIFNode childNode) { childNodes.add(childNode); } /** * Retrieves a string representation of this LDIF node. It will indicate * how many immediate children it has. Those children that have their own * subordinate entries will be enumerated in an indented fashion on their * own line (may be several levels deep through recursion). * * @return A string representation of this LDIF node. */ public String toString() { return toString(0, 2); } /** * Retrieves a string representation of this LDIF node starting with the * specified indent. * * @param indent The number of spaces to indent the information for this * node. * @param increment The incremental depth to use for descendant nodes. * * @return A string representation of this LDIF node starting with the * specified indent. */ public String toString(int indent, int increment) { StringBuilder buffer = new StringBuilder(); for (int i=0; i < indent; i++) { buffer.append(' '); } buffer.append(normalizedDN); buffer.append(": "); buffer.append(numChildren); if (numChildren == 1) { buffer.append(" immediate child"); } else { buffer.append(" immediate children"); } String EOL = System.getProperty("line.separator"); for (int i=0; i < childNodes.size(); i++) { LDIFNode n = childNodes.get(i); buffer.append(EOL); buffer.append(n.toString(indent+increment, increment)); } return buffer.toString(); } }