com.helger.tree.util.TreeWithIDBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ph-tree Show documentation
Show all versions of ph-tree Show documentation
Java 1.8+ Library with tree structures
The newest version!
/*
* Copyright (C) 2014-2024 Philip Helger (www.helger.com)
* philip[at]helger[dot]com
*
* 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.
*/
package com.helger.tree.util;
import java.util.Collection;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.PresentForCodeCoverage;
import com.helger.commons.collection.impl.CommonsArrayList;
import com.helger.commons.collection.impl.CommonsHashMap;
import com.helger.commons.collection.impl.ICommonsMap;
import com.helger.commons.hierarchy.IChildrenProvider;
import com.helger.commons.hierarchy.IHasParent;
import com.helger.commons.hierarchy.IParentProvider;
import com.helger.commons.id.IHasID;
import com.helger.tree.withid.DefaultTreeItemWithID;
import com.helger.tree.withid.DefaultTreeWithID;
/**
* Utility classes for building a tree from flat collections.
*
* @author Philip Helger
*/
@Immutable
public final class TreeWithIDBuilder
{
@PresentForCodeCoverage
private static final TreeWithIDBuilder INSTANCE = new TreeWithIDBuilder ();
private TreeWithIDBuilder ()
{}
@Nonnull
private static > DefaultTreeWithID _buildTree (@Nonnull final List aOpen,
@Nonnull final IParentProvider aParentResolver)
{
final DefaultTreeWithID aTree = new DefaultTreeWithID <> ();
final ICommonsMap > aIDMap = new CommonsHashMap <> ();
int nMovedToBackCount = 0;
while (!aOpen.isEmpty ())
{
// get first element
final DATATYPE aCurrent = aOpen.remove (0);
final DATATYPE aParent = aParentResolver.getParent (aCurrent);
final KEYTYPE aCurrentID = aCurrent.getID ();
if (aParent == null)
{
// it is a root item
final DefaultTreeItemWithID aNewItem = aTree.getRootItem ().createChildItem (aCurrentID, aCurrent);
aIDMap.put (aCurrentID, aNewItem);
nMovedToBackCount = 0;
}
else
{
final KEYTYPE aParentID = aParent.getID ();
if (aIDMap.containsKey (aParentID))
{
// it's a subordinated ID
final DefaultTreeItemWithID aParentItem = aIDMap.get (aParentID);
final DefaultTreeItemWithID aNewItem = aParentItem.createChildItem (aCurrentID, aCurrent);
aIDMap.put (aCurrentID, aNewItem);
nMovedToBackCount = 0;
}
else
{
// element is unknown -> move element to the end
aOpen.add (aCurrent);
nMovedToBackCount++;
if (nMovedToBackCount == aOpen.size ())
throw new IllegalStateException ("The hierarchy is illegal. It contains elements that fit nowhere in the tree: " + aOpen);
}
}
}
return aTree;
}
/**
* A generic method to build a tree of objects.
*
* @param
* The tree key type.
* @param
* The tree item value type.
* @param aAll
* A linear list of objects to build the tree from. May not be
* null
.
* @param aParentResolver
* The callback method to determine the parental object of a given
* object. May not be null
.
* @return A tree with all the objects. Never null
.
* @throws IllegalStateException
* if the hierarchy cannot be determined because an object references
* a parent that is not in the list!
*/
@Nonnull
public static > DefaultTreeWithID buildTree (@Nonnull final Collection extends DATATYPE> aAll,
@Nonnull final IParentProvider aParentResolver)
{
ValueEnforcer.notNull (aAll, "All");
ValueEnforcer.notNull (aParentResolver, "ParentResolver");
return _buildTree (new CommonsArrayList <> (aAll), aParentResolver);
}
/**
* A generic method to build a tree of objects.
*
* @param
* The tree key type.
* @param
* The tree item value type.
* @param aAll
* A linear list of objects to build the tree from. May not be
* null
.
* @param aParentResolver
* The callback method to determine the parental object of a given
* object. May not be null
.
* @return A tree with all the objects. Never null
.
* @throws IllegalStateException
* if the hierarchy cannot be determined because an object references
* a parent that is not in the list!
*/
@Nonnull
public static > DefaultTreeWithID buildTree (@Nonnull final DATATYPE [] aAll,
@Nonnull final IParentProvider aParentResolver)
{
ValueEnforcer.notNull (aAll, "All");
ValueEnforcer.notNull (aParentResolver, "ParentResolver");
return _buildTree (new CommonsArrayList <> (aAll), aParentResolver);
}
/**
* A generic method to build a tree of objects.
*
* @param
* The tree key type.
* @param
* The tree item value type.
* @param aAll
* A linear list of objects to build the tree from. May not be
* null
.
* @return A tree with all the objects. Never null
.
* @throws IllegalStateException
* if the hierarchy cannot be determined because an object references
* a parent that is not in the list!
*/
@Nonnull
public static & IHasID > DefaultTreeWithID buildTree (@Nonnull final Collection extends DATATYPE> aAll)
{
ValueEnforcer.notNull (aAll, "All");
return buildTree (aAll, IParentProvider.parentProviderHasParent ());
}
private static > void _buildTreeRecursive (@Nullable final DefaultTreeItemWithID aParentItem,
@Nonnull final IChildrenProvider aChildrenResolver)
{
if (aParentItem != null)
{
final DATATYPE aParentObject = aParentItem.getData ();
if (aChildrenResolver.hasChildren (aParentObject))
for (final DATATYPE aChild : aChildrenResolver.getAllChildren (aParentObject))
{
final DefaultTreeItemWithID aItem = aParentItem.createChildItem (aChild.getID (), aChild);
_buildTreeRecursive (aItem, aChildrenResolver);
}
}
}
@Nonnull
public static > DefaultTreeWithID buildTree (@Nonnull final IChildrenProvider aChildrenResolver)
{
ValueEnforcer.notNull (aChildrenResolver, "ChildrenResolver");
final DefaultTreeWithID aTree = new DefaultTreeWithID <> ();
// get all root objects
if (aChildrenResolver.hasChildren (null))
for (final DATATYPE aRootObject : aChildrenResolver.getAllChildren (null))
{
// it is a root item
final DefaultTreeItemWithID aItem = aTree.getRootItem ().createChildItem (aRootObject.getID (), aRootObject);
_buildTreeRecursive (aItem, aChildrenResolver);
}
return aTree;
}
}