All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.helger.diver.repo.toc.RepoTopTocXML Maven / Gradle / Ivy

/*
 * Copyright (C) 2023-2024 Philip Helger & ecosio
 * 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.diver.repo.toc;

import java.util.Map;
import java.util.function.Consumer;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.Nonempty;
import com.helger.commons.collection.impl.CommonsTreeMap;
import com.helger.commons.collection.impl.CommonsTreeSet;
import com.helger.commons.collection.impl.ICommonsList;
import com.helger.commons.collection.impl.ICommonsSortedMap;
import com.helger.commons.collection.impl.ICommonsSortedSet;
import com.helger.commons.state.EChange;
import com.helger.commons.string.StringHelper;
import com.helger.diver.api.version.VESID;
import com.helger.diver.repo.RepoStorageKeyOfArtefact;
import com.helger.diver.repo.toptoc.jaxb.v10.ArtifactType;
import com.helger.diver.repo.toptoc.jaxb.v10.GroupType;
import com.helger.diver.repo.toptoc.jaxb.v10.RepoTopTocType;

/**
 * JAXB independent top ToC representation
 *
 * @author Philip Helger
 */
public class RepoTopTocXML
{
  /**
   * Represents a single group with a name, a list of sub groups and a list of
   * artifacts.
   *
   * @author Philip Helger
   */
  private static final class Group
  {
    private final String m_sName;
    private final ICommonsSortedMap  m_aSubGroups = new CommonsTreeMap <> ();
    private final ICommonsSortedSet  m_aArtifacts = new CommonsTreeSet <> ();

    public Group (@Nonnull @Nonempty final String sName)
    {
      ValueEnforcer.notEmpty (sName, "Name");
      ValueEnforcer.isTrue ( () -> VESID.isValidGroupID (sName), "Name is not a valid group part");
      m_sName = sName;
    }

    public boolean deepEquals (@Nonnull final Group rhs)
    {
      // Check all fields - takes longer
      return m_sName.equals (rhs.m_sName) &&
             m_aSubGroups.equals (rhs.m_aSubGroups) &&
             m_aArtifacts.equals (rhs.m_aArtifacts);
    }

    @Nonnull
    public GroupType getAsJaxbObject ()
    {
      final GroupType ret = new GroupType ();
      ret.setName (m_sName);

      // Hack: Make sure the internal lists are created (important for equals)
      ret.getArtifact ();
      ret.getGroup ();

      for (final String sArtifactID : m_aArtifacts)
      {
        final ArtifactType aJaxbArtifact = new ArtifactType ();
        aJaxbArtifact.setName (sArtifactID);
        ret.addArtifact (aJaxbArtifact);
      }

      // recursive descend
      for (final Group aSubGroup : m_aSubGroups.values ())
        ret.addGroup (aSubGroup.getAsJaxbObject ());
      return ret;
    }
  }

  private final ICommonsSortedMap  m_aTopLevelGroups = new CommonsTreeMap <> ();

  public RepoTopTocXML ()
  {}

  public int getTopLevelGroupCount ()
  {
    return m_aTopLevelGroups.size ();
  }

  @Nullable
  private Group _getGroup (@Nonnull @Nonempty final String sGroupID)
  {
    final ICommonsList  aGroupPart = StringHelper.getExploded (RepoStorageKeyOfArtefact.GROUP_LEVEL_SEPARATOR,
                                                                       sGroupID);
    // Resolve all recursive subgroups
    Group aGroup = m_aTopLevelGroups.get (aGroupPart.removeFirstOrNull ());
    while (aGroup != null && aGroupPart.isNotEmpty ())
    {
      aGroup = aGroup.m_aSubGroups.get (aGroupPart.removeFirstOrNull ());
    }
    return aGroup;
  }

  public boolean containsGroupAndArtifact (@Nonnull @Nonempty final String sGroupID,
                                           @Nonnull @Nonempty final String sArtifactID)
  {
    ValueEnforcer.notEmpty (sGroupID, "GroupID");
    ValueEnforcer.notEmpty (sArtifactID, "ArtifactID");

    final Group aGroup = _getGroup (sGroupID);
    if (aGroup == null)
      return false;

    return aGroup.m_aArtifacts.contains (sArtifactID);
  }

  public void iterateAllTopLevelGroupNames (@Nonnull final Consumer  aGroupNameConsumer)
  {
    ValueEnforcer.notNull (aGroupNameConsumer, "GroupNameConsumer");

    m_aTopLevelGroups.keySet ().forEach (aGroupNameConsumer);
  }

  private void _recursiveIterateExistingSubGroups (@Nonnull @Nonempty final String sAbsoluteGroupID,
                                                   @Nonnull final Group aCurGroup,
                                                   @Nonnull final IRepoTopTocGroupNameConsumer aGroupNameConsumer,
                                                   final boolean bRecursive)
  {
    for (final Map.Entry  aEntry : aCurGroup.m_aSubGroups.entrySet ())
    {
      final String sSubGroupName = aEntry.getKey ();
      final String sSubGroupAbsoluteName = sAbsoluteGroupID +
                                           RepoStorageKeyOfArtefact.GROUP_LEVEL_SEPARATOR +
                                           sSubGroupName;
      aGroupNameConsumer.accept (sSubGroupName, sSubGroupAbsoluteName);

      // Descend always or never
      if (bRecursive)
        _recursiveIterateExistingSubGroups (sSubGroupAbsoluteName, aEntry.getValue (), aGroupNameConsumer, true);
    }
  }

  public void iterateAllSubGroups (@Nonnull @Nonempty final String sGroupID,
                                   @Nonnull final IRepoTopTocGroupNameConsumer aGroupNameConsumer,
                                   final boolean bRecursive)
  {
    ValueEnforcer.notEmpty (sGroupID, "GroupID");
    ValueEnforcer.notNull (aGroupNameConsumer, "GroupNameConsumer");

    final Group aGroup = _getGroup (sGroupID);
    if (aGroup != null)
    {
      _recursiveIterateExistingSubGroups (sGroupID, aGroup, aGroupNameConsumer, bRecursive);
    }
    // else: no group, no callback
  }

  public void iterateAllArtifacts (@Nonnull @Nonempty final String sGroupID,
                                   @Nonnull final Consumer  aArtifactNameConsumer)
  {
    ValueEnforcer.notEmpty (sGroupID, "GroupID");
    ValueEnforcer.notNull (aArtifactNameConsumer, "ArtifactNameConsumer");

    final Group aGroup = _getGroup (sGroupID);
    if (aGroup != null)
    {
      for (final String sArtifactID : aGroup.m_aArtifacts)
        aArtifactNameConsumer.accept (sArtifactID);
    }
    // else: no group, no callback
  }

  @Nonnull
  private Group _getOrCreateGroup (@Nonnull @Nonempty final String sGroupID)
  {
    final ICommonsList  aGroupPart = StringHelper.getExploded (RepoStorageKeyOfArtefact.GROUP_LEVEL_SEPARATOR,
                                                                       sGroupID);
    // Resolve all recursive subgroups
    Group aGroup = m_aTopLevelGroups.computeIfAbsent (aGroupPart.removeFirstOrNull (), Group::new);
    while (aGroupPart.isNotEmpty ())
    {
      aGroup = aGroup.m_aSubGroups.computeIfAbsent (aGroupPart.removeFirstOrNull (), Group::new);
    }
    return aGroup;
  }

  @Nonnull
  public EChange registerGroupAndArtifact (@Nonnull @Nonempty final String sGroupID,
                                           @Nonnull @Nonempty final String sArtifactID)
  {
    ValueEnforcer.notEmpty (sGroupID, "GroupID");
    ValueEnforcer.notEmpty (sArtifactID, "ArtifactID");

    final Group aGroup = _getOrCreateGroup (sGroupID);

    // Add to artifact list of latest subgroup
    final boolean bAdded = aGroup.m_aArtifacts.add (sArtifactID);

    return EChange.valueOf (bAdded);
  }

  public boolean deepEquals (@Nonnull final RepoTopTocXML aOther)
  {
    ValueEnforcer.notNull (aOther, "Other");

    // Size must match
    if (m_aTopLevelGroups.size () != aOther.m_aTopLevelGroups.size ())
      return false;

    for (final Map.Entry  aEntry : m_aTopLevelGroups.entrySet ())
    {
      final Group aOtherGroup = aOther.m_aTopLevelGroups.get (aEntry.getKey ());
      if (aOtherGroup == null)
      {
        // Only present in this but not in other
        return false;
      }

      final Group aThisGroup = aEntry.getValue ();
      if (!aThisGroup.deepEquals (aOtherGroup))
      {
        // Groups differ
        return false;
      }
    }
    return true;
  }

  @Nonnull
  public RepoTopTocType getAsJaxbObject ()
  {
    final RepoTopTocType ret = new RepoTopTocType ();
    for (final Map.Entry  aEntry : m_aTopLevelGroups.entrySet ())
      ret.addGroup (aEntry.getValue ().getAsJaxbObject ());
    return ret;
  }

  private static void _recursiveReadGroups (@Nonnull final String sAbsoluteGroupName,
                                            @Nonnull final GroupType aSrcGroup,
                                            @Nonnull final Group aDstGroup)
  {
    // First add artifacts
    for (final ArtifactType aSrcArtifact : aSrcGroup.getArtifact ())
    {
      final String sArtifactName = aSrcArtifact.getName ();
      if (!aDstGroup.m_aArtifacts.add (sArtifactName))
        throw new IllegalStateException ("The artifact '" +
                                         sAbsoluteGroupName +
                                         ':' +
                                         sArtifactName +
                                         "' is contained more then once");
    }

    // Now all subgroups
    for (final GroupType aSrcSubGroup : aSrcGroup.getGroup ())
    {
      final String sSubGroupName = aSrcSubGroup.getName ();
      final Group aDstSubGroup = new Group (sSubGroupName);
      if (aDstGroup.m_aSubGroups.put (sSubGroupName, aDstSubGroup) != null)
        throw new IllegalArgumentException ("Another group with name '" +
                                            sAbsoluteGroupName +
                                            RepoStorageKeyOfArtefact.GROUP_LEVEL_SEPARATOR +
                                            sSubGroupName +
                                            "' is already contained");

      // Descend recursively
      _recursiveReadGroups (sAbsoluteGroupName + RepoStorageKeyOfArtefact.GROUP_LEVEL_SEPARATOR + sSubGroupName,
                            aSrcSubGroup,
                            aDstSubGroup);
    }
  }

  @Nonnull
  public static RepoTopTocXML createFromJaxbObject (@Nonnull final RepoTopTocType aRepoTopToc)
  {
    ValueEnforcer.notNull (aRepoTopToc, "RepoTopToc");

    final RepoTopTocXML ret = new RepoTopTocXML ();
    for (final GroupType aSrcGroup : aRepoTopToc.getGroup ())
    {
      final String sGroupName = aSrcGroup.getName ();
      final Group aDstGroup = new Group (sGroupName);
      if (ret.m_aTopLevelGroups.put (sGroupName, aDstGroup) != null)
        throw new IllegalArgumentException ("Another top-level group with name '" +
                                            sGroupName +
                                            "' is already contained");
      _recursiveReadGroups (sGroupName, aSrcGroup, aDstGroup);
    }
    return ret;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy