/* * Copyright 2000-2013 Enonic AS * http://www.enonic.com/license */ package com.enonic.esl.util; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; /** * This class implements the relation tree. */ public final class RelationTree { private final static class Entry implements RelationNode { private final Object key; private Entry parent; private final List<RelationNode> children; private Object data; private boolean selected; private EntrySet set; private Entry() { this( null ); } private Entry( Object key ) { this.key = key; this.parent = null; this.children = new LinkedList<RelationNode>(); this.data = null; this.selected = false; } public Object getKey() { return this.key; } public RelationNode getParent() { return this.parent; } public boolean isRoot() { return this.parent == null; } public boolean hasParent() { return this.parent != null; } public boolean hasChildren() { return !this.children.isEmpty(); } public int getChildCount() { return this.children.size(); } public int getTotalChildCount() { int count = this.children.size(); for ( RelationNode aChildren : this.children ) { count += aChildren.getTotalChildCount(); } return count; } public List getChildren() { return Collections.unmodifiableList( this.children ); } public boolean isAncestor( RelationNode node ) { return this.parent != null && ( this.parent == node || this.parent.isAncestor( node ) ); } public boolean isAncestorOrSelf( RelationNode node ) { return isAncestor( node ) || ( node == this ); } public int getLevel() { int level = 0; if ( this.parent != null ) { level = this.parent.getLevel() + 1; } return level; } public boolean isChild( RelationNode node ) { return this.children.contains( node ); } public Object accept( RelationVisitor visitor ) { if ( visitor != null ) { return visitor.visit( this ); } else { return null; } } public Object getData() { return this.data; } public void setData( Object data ) { this.data = data; } public void addChild( Entry entry ) { if ( !isAncestorOrSelf( entry ) ) { entry.parent = this; this.children.add( entry ); clearEntrySet(); } } public RelationNode getRoot() { return getRootEntry(); } private Entry getRootEntry() { if ( this.parent != null ) { return this.parent.getRootEntry(); } else { return this; } } private EntrySet getEntrySet() { Entry root = getRootEntry(); if ( root.set == null ) { root.set = new EntrySet(); root.set.addEntry( root ); } return root.set; } private void clearEntrySet() { Entry root = getRootEntry(); if ( root.set != null ) { root.set = null; } } public RelationNode getNode( Object key ) { if ( key == null ) { return getRootEntry(); } else { return getEntrySet().getEntry( key ); } } public boolean isSelected() { return this.selected; } public void setSelected( boolean selected ) { this.selected = selected; } public void selectLevels( int levels ) { if ( levels >= 0 ) { setSelected( true ); for ( Object aChildren : this.children ) { ( (Entry) aChildren ).selectLevels( levels - 1 ); } } } public void selectAll() { selectLevels( Integer.MAX_VALUE ); } public void clearSelected() { setSelected( false ); for ( Object aChildren : this.children ) { ( (Entry) aChildren ).clearSelected(); } } public Set findSelectedKeys() { HashSet set = new HashSet(); findSelected( set, true ); return set; } public Set findSelectedNodes() { HashSet set = new HashSet(); findSelected( set, false ); return set; } private void findSelected( Set set, boolean keys ) { if ( this.selected ) { if ( this.key != null ) { set.add( keys ? this.key : this ); } } for ( Object aChildren : this.children ) { ( (Entry) aChildren ).findSelected( set, keys ); } } public int hashCode() { return this.key.hashCode(); } } private final static class EntrySet { private final Map<Object, Entry> map; public EntrySet() { this.map = new HashMap<Object, Entry>(); } public void addEntry( Entry entry ) { addEntry( entry.key, entry ); addEntries( entry.children ); } public void addEntries( Collection entries ) { if ( entries != null ) { for ( Object entry : entries ) { addEntry( (Entry) entry ); } } } private void addEntry( Object key, Entry entry ) { if ( key != null ) { this.map.put( key, entry ); } } public Entry getEntry( Object key ) { return this.map.get( key ); } } private final HashMap<Object, Object> keyMap; private final LinkedList<Object> orderedChildrenList; private RelationNode root; public RelationTree() { this.keyMap = new HashMap<Object, Object>(); this.orderedChildrenList = new LinkedList<Object>(); } public RelationNode getRoot() { if ( this.root == null ) { this.root = buildRoot( this.keyMap, this.orderedChildrenList ); } return this.root; } public RelationNode getNode( Object key ) { RelationNode root = getRoot(); if ( key != null ) { return root.getNode( key ); } else { return root; } } private static RelationNode buildRoot( Map keys, List orderList ) { // Create nodes HashMap<Object, Entry> nodeMap = new HashMap<Object, Entry>(); for ( Object key : keys.keySet() ) { if ( !nodeMap.containsKey( key ) ) { nodeMap.put( key, new Entry( key ) ); } } // Attach nodes to parent Entry root = new Entry(); for ( Object key : orderList ) { Object parent = keys.get( key ); Entry keyEntry = nodeMap.get( key ); Entry parentEntry = root; if ( parent != null ) { parentEntry = nodeMap.get( parent ); } if ( parentEntry != null ) { parentEntry.addChild( keyEntry ); } } return root; } public boolean addChild( Object child ) { return addChild( null, child ); } public boolean addChild( Object parent, Object child ) { if ( this.keyMap.containsKey( child ) ) { return false; } else { this.keyMap.put( child, parent ); this.orderedChildrenList.add( child ); this.root = null; return true; } } public boolean setRoot( Object key ) { if ( this.keyMap.containsKey( key ) ) { this.keyMap.put( key, null ); return true; } else { return false; } } }