Skip to content

Commit

Permalink
Create abstract TopToc data model
Browse files Browse the repository at this point in the history
  • Loading branch information
phax committed Nov 28, 2023
1 parent 929d3da commit 0fd69f3
Show file tree
Hide file tree
Showing 6 changed files with 327 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public final class RepoStorageKey
*/
public static final String FILENAME_TOC_DIVER_XML = "toc-diver.xml";

public static final char GROUP_LEVEL_SEPARATOR = '.';

private static final Logger LOGGER = LoggerFactory.getLogger (RepoStorageKey.class);

// Special fake version to be used by the ToC where we don't need any version
Expand Down Expand Up @@ -144,7 +146,7 @@ public static String getPathOfGroupIDAndArtifactID (@Nonnull @Nonempty final Str
ValueEnforcer.notEmpty (sGroupID, "GroupID");
ValueEnforcer.notEmpty (sArtifactID, "ArtifactID");

return sGroupID.replace ('.', '/') + "/" + sArtifactID + "/";
return sGroupID.replace (GROUP_LEVEL_SEPARATOR, '/') + "/" + sArtifactID + "/";
}

/**
Expand Down
227 changes: 227 additions & 0 deletions ph-diver-repo/src/main/java/com/helger/diver/repo/toc/RepoTopToc.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
package com.helger.diver.repo.toc;

import java.util.Map;
import java.util.function.BiConsumer;
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.annotation.ReturnsMutableCopy;
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.string.StringHelper;
import com.helger.diver.api.version.VESID;
import com.helger.diver.repo.RepoStorageKey;
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 RepoTopToc
{
private static final class Group
{
private final String m_sName;
private final ICommonsSortedMap <String, Group> m_aSubGroups = new CommonsTreeMap <> ();
private final ICommonsSortedSet <String> m_aArtifacts = new CommonsTreeSet <> ();

public Group (@Nonnull @Nonempty final String sName)
{
ValueEnforcer.notEmpty (sName, "Name");
ValueEnforcer.isTrue ( () -> VESID.isValidPart (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);
}
}

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

public RepoTopToc ()
{}

@Nonnull
@ReturnsMutableCopy
public ICommonsSortedSet <String> getAllTopLevelGroupNames ()
{
return m_aTopLevelGroups.copyOfKeySet ();
}

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

private void _recursiveIterateExistingSubGroups (@Nonnull @Nonempty final String sAbsoluteGroupID,
@Nonnull final Group aCurGroup,
@Nonnull final BiConsumer <String, String> aGroupNameConsumer)
{
for (final Map.Entry <String, Group> aEntry : aCurGroup.m_aSubGroups.entrySet ())
{
final String sSubGroupName = aEntry.getKey ();
final String sSubAbsName = sAbsoluteGroupID + RepoStorageKey.GROUP_LEVEL_SEPARATOR + sSubGroupName;
aGroupNameConsumer.accept (sSubGroupName, sSubAbsName);

// Descend
_recursiveIterateExistingSubGroups (sSubAbsName, aEntry.getValue (), aGroupNameConsumer);
}
}

public void iterateAllSubGroups (@Nonnull @Nonempty final String sGroupID,
@Nonnull final BiConsumer <String, String> aGroupNameConsumer)
{
ValueEnforcer.notEmpty (sGroupID, "GroupID");
ValueEnforcer.notNull (aGroupNameConsumer, "GroupNameConsumer");

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

public void iterateAllArtifacts (@Nonnull @Nonempty final String sGroupID,
@Nonnull final Consumer <String> 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 <String> aGroupPart = StringHelper.getExploded (RepoStorageKey.GROUP_LEVEL_SEPARATOR, sGroupID);
// Resolve all recursive subgroups
Group aGroup = m_aTopLevelGroups.computeIfAbsent (aGroupPart.removeFirst (), Group::new);
while (aGroupPart.isNotEmpty ())
{
aGroup = aGroup.m_aSubGroups.computeIfAbsent (aGroupPart.removeFirst (), Group::new);
}
return aGroup;
}

public void 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
aGroup.m_aArtifacts.add (sArtifactID);
}

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

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

for (final Map.Entry <String, Group> 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;
}

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 +
RepoStorageKey.GROUP_LEVEL_SEPARATOR +
sSubGroupName +
"' is already contained");

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

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

final RepoTopToc ret = new RepoTopToc ();
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.helger.diver.repo.toptoc;
package com.helger.diver.repo.toc;

import com.helger.commons.collection.impl.CommonsArrayList;
import com.helger.commons.io.resource.ClassPathResource;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.helger.diver.repo.toptoc;
package com.helger.diver.repo.toc;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
Expand Down Expand Up @@ -53,7 +53,7 @@ public void testRepoToc1 ()
assertEquals (2, aCom.getGroupAtIndex (0).getArtifactCount ());
assertEquals (0, aCom.getGroupAtIndex (1).getGroupCount ());
assertEquals (1, aCom.getGroupAtIndex (1).getArtifactCount ());
assertEquals (0, aCom.getGroupAtIndex (2).getGroupCount ());
assertEquals (1, aCom.getGroupAtIndex (2).getGroupCount ());
assertEquals (0, aCom.getGroupAtIndex (2).getArtifactCount ());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (C) 2023 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import org.junit.Test;

import com.helger.commons.collection.impl.CommonsArrayList;
import com.helger.commons.collection.impl.CommonsHashSet;
import com.helger.commons.collection.impl.ICommonsList;
import com.helger.commons.collection.impl.ICommonsSet;
import com.helger.commons.collection.impl.ICommonsSortedSet;
import com.helger.commons.io.resource.ClassPathResource;
import com.helger.diver.repo.toptoc.jaxb.v10.RepoTopTocType;

/**
* Test class for class {@link RepoTopToc}.
*
* @author Philip Helger
*/
public final class RepoTopTocTest
{
@Test
public void testBasic ()
{}

@Test
public void testCreateFromJaxb ()
{
final RepoTopToc1Marshaller m = new RepoTopToc1Marshaller ();
final RepoTopTocType aRepoTopToc1 = m.read (new ClassPathResource ("repotoptoc/repotoptoc-1.xml"));
assertNotNull (aRepoTopToc1);

final RepoTopToc aToC = RepoTopToc.createFromJaxbObject (aRepoTopToc1);
assertNotNull (aToC);

// Check top level groups
final ICommonsSortedSet <String> aTLGroups = aToC.getAllTopLevelGroupNames ();
assertNotNull (aTLGroups);
assertEquals (2, aTLGroups.size ());
assertTrue (aTLGroups.contains ("com"));
assertTrue (aTLGroups.contains ("org"));

// Check all subgroups from a specific start
final ICommonsList <String> aAllRelSubgroups = new CommonsArrayList <> ();
final ICommonsSet <String> aAllAbsSubgroups = new CommonsHashSet <> ();
aToC.iterateAllSubGroups ("com", (relgn, absgn) -> {
aAllRelSubgroups.add (relgn);
aAllAbsSubgroups.add (absgn);
});
assertEquals (6, aAllAbsSubgroups.size ());
assertTrue (aAllAbsSubgroups.contains ("com.ecosio"));
assertTrue (aAllAbsSubgroups.contains ("com.helger"));
assertTrue (aAllAbsSubgroups.contains ("com.rest"));
assertTrue (aAllAbsSubgroups.contains ("com.rest.of"));
assertTrue (aAllAbsSubgroups.contains ("com.rest.of.the"));
assertTrue (aAllAbsSubgroups.contains ("com.rest.of.the.fest"));

assertEquals (6, aAllRelSubgroups.size ());
assertEquals ("ecosio", aAllRelSubgroups.get (0));
assertEquals ("helger", aAllRelSubgroups.get (1));
assertEquals ("rest", aAllRelSubgroups.get (2));
assertEquals ("of", aAllRelSubgroups.get (3));
assertEquals ("the", aAllRelSubgroups.get (4));
assertEquals ("fest", aAllRelSubgroups.get (5));

final ICommonsSet <String> aAllArtifacts = new CommonsHashSet <> ();
aToC.iterateAllArtifacts ("com", artifactID -> { aAllArtifacts.add (artifactID); });
}
}
8 changes: 7 additions & 1 deletion ph-diver-repo/src/test/resources/repotoptoc/repotoptoc-1.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@
<artifact name="artifact3" />
</group>
<group name="rest">
<!-- empty -->
<group name="of">
<group name="the">
<group name="fest">
<!-- empty -->
</group>
</group>
</group>
</group>
</group>
<group name="org">
Expand Down

0 comments on commit 0fd69f3

Please sign in to comment.