Skip to content

Commit

Permalink
Updates for PFUOI 4.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
phax committed Nov 7, 2024
1 parent 75cadc6 commit 0277cc5
Show file tree
Hide file tree
Showing 7 changed files with 987 additions and 464 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,9 @@ They depend on several other libraries so I suggest you are going for the Maven

# News and noteworthy

* v9.6.0 - 2024-10-16
* Deprecated methods `SMPClientReadOnly.getCompleteServiceGroup(OrNull)` and `getServiceGroupReferenceList(OrNull)` because the underlying APIs are non-standard
* Extracted methods from `ISMPServiceMetadataProvider` into `ISMPExtendedServiceMetadataProvider`
* v9.5.1 - 2024-08-11
* Make sure that wildcard lookups including a "*" in the Customization ID will always fail
* Added additional `SMPClientReadOnly.getWildcardServiceMetadataOrNull` overload
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.helger.peppol.supplementary.tools;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.function.Predicate;
import java.util.regex.Pattern;

import org.junit.Test;

import com.helger.commons.regex.RegExHelper;

public class RegExFuncTest
{
@Test
public void testRegExSPIS ()
{
final String sRegEx = "[0-9]{6}(-[0-9A-Z_]{3,12}(\\.[0-9A-Z\\-\\._~]{3,24})?)?";

final Predicate <String> pred = x -> RegExHelper.stringMatchesPattern (sRegEx, Pattern.CASE_INSENSITIVE, x);

assertTrue (pred.test ("000001"));
assertTrue (pred.test ("000270"));
assertTrue (pred.test ("010101"));
assertTrue (pred.test ("999999"));
assertFalse (pred.test ("99999"));
assertFalse (pred.test ("a99999"));
assertFalse (pred.test ("9999999"));

assertTrue (pred.test ("000001-AAA"));
assertTrue (pred.test ("000001-Rprtng_MLS"));
assertTrue (pred.test ("000270-1234567"));
assertFalse (pred.test ("000270.12"));
assertFalse (pred.test ("000270.1234567"));
assertFalse (pred.test ("0002701234567"));
assertFalse (pred.test ("000270--1234567"));

assertTrue (pred.test ("000001-MLS.001"));
assertTrue (pred.test ("000001-001.1234567"));
assertTrue (pred.test ("000270-Rprtng_MLS.Japan.123"));
}
}

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.helger.smpclient.peppol;

import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

Expand All @@ -28,11 +29,14 @@
import org.slf4j.LoggerFactory;

import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.DevelopersNote;
import com.helger.commons.annotation.Nonempty;
import com.helger.commons.collection.impl.ICommonsList;
import com.helger.commons.id.IHasID;
import com.helger.commons.lang.EnumHelper;
import com.helger.commons.state.EContinue;
import com.helger.commons.state.ESuccess;
import com.helger.commons.string.StringHelper;
import com.helger.commons.string.ToStringGenerator;
import com.helger.peppolid.CIdentifier;
import com.helger.peppolid.IDocumentTypeIdentifier;
Expand All @@ -57,6 +61,8 @@ public class PeppolWildcardSelector
*
* @author Philip Helger
*/
@Deprecated
@DevelopersNote ("This was valid for Policy for use of Identifiers 4.2.0. This is no longer valid with PFUOI 4.3.0 from May 15th 2025")
public enum EMode implements IHasID <String>
{
/**
Expand Down Expand Up @@ -110,6 +116,8 @@ public static EMode getFromIDOrNull (@Nullable final String sID)
* @param eMode
* The selection mode to use. May not be <code>null</code>.
*/
@Deprecated
@DevelopersNote ("This was valid for Policy for use of Identifiers 4.2.0. This is no longer valid with PFUOI 4.3.0 from May 15th 2025")
public PeppolWildcardSelector (@Nonnull final EMode eMode)
{
ValueEnforcer.notNull (eMode, "Mode");
Expand All @@ -120,14 +128,16 @@ public PeppolWildcardSelector (@Nonnull final EMode eMode)
* @return The selection mode as provided in the constructor.
*/
@Nonnull
@Deprecated
@DevelopersNote ("This was valid for Policy for use of Identifiers 4.2.0. This is no longer valid with PFUOI 4.3.0 from May 15th 2025")
public final EMode getMode ()
{
return m_eMode;
}

/**
* Helper method to iterate all matching document type identifiers. The
* preference of schemes is defined by the operational mode.
* Helper method to iterate all matching document type identifiers for PFUOI
* 4.2.0. The preference of schemes is defined by the operational mode.
*
* @param aBaseDocTypes
* The list of document types to filter. Usually this list was obtained
Expand All @@ -140,6 +150,8 @@ public final EMode getMode ()
* The consumer to be invoked for each match. May not be
* <code>null</code>.
*/
@Deprecated
@DevelopersNote ("This was valid for Policy for use of Identifiers 4.2.0. This is no longer valid with PFUOI 4.3.0 from May 15th 2025")
public void forEachMatchingDocumentType (@Nonnull final ICommonsList <? extends IDocumentTypeIdentifier> aBaseDocTypes,
@Nonnull @Nonempty final String sDocTypeValue,
@Nonnull final Function <? super IDocumentTypeIdentifier, EContinue> aMatchingDocTypeConsumer)
Expand All @@ -149,15 +161,17 @@ public void forEachMatchingDocumentType (@Nonnull final ICommonsList <? extends
ValueEnforcer.notNull (aMatchingDocTypeConsumer, "MatchingDocTypeConsumer");

final BiFunction <String, String, IDocumentTypeIdentifier> aFuncCheckExistance = (sScheme, sValue) -> {
if (LOGGER.isInfoEnabled ())
LOGGER.info ("Checking if document type ID '" + CIdentifier.getURIEncoded (sScheme, sValue) + "' is contained");
if (LOGGER.isDebugEnabled ())
LOGGER.debug ("Checking if document type ID '" +
CIdentifier.getURIEncoded (sScheme, sValue) +
"' is contained");
if (aBaseDocTypes.containsAny (x -> x.hasScheme (sScheme) && x.hasValue (sValue)))
return PeppolIdentifierFactory.INSTANCE.createDocumentTypeIdentifier (sScheme, sValue);
return null;
};

// Try busdox-docid-qns ("as is")
final Supplier <EContinue> aBusdoxMatch = () -> {
// Try busdox-docid-qns exact match (PFUOI 4.2 / 4.3)
final Supplier <EContinue> aBusdoxExactMatch = () -> {
final IDocumentTypeIdentifier aSelectedDocTypeID = aFuncCheckExistance.apply (PeppolIdentifierHelper.DOCUMENT_TYPE_SCHEME_BUSDOX_DOCID_QNS,
sDocTypeValue);
if (aSelectedDocTypeID != null)
Expand All @@ -167,8 +181,8 @@ public void forEachMatchingDocumentType (@Nonnull final ICommonsList <? extends
return EContinue.CONTINUE;
};

// Try wildcard match (new PINT style logic)
final Supplier <EContinue> aWildcardMatch = () -> {
// Try peppold-doctype-wildcard best match (PFUOI 4.2 / 4.3)
final Supplier <EContinue> aWildcardBestMatch = () -> {
try
{
// Split the document type identifier value into pieces (throws
Expand Down Expand Up @@ -222,24 +236,127 @@ public void forEachMatchingDocumentType (@Nonnull final ICommonsList <? extends
switch (m_eMode)
{
case BUSDOX_ONLY:
aBusdoxMatch.get ();
aBusdoxExactMatch.get ();
break;
case WILDCARD_ONLY:
aWildcardMatch.get ();
aWildcardBestMatch.get ();
break;
case BUSDOX_THEN_WILDCARD:
if (aBusdoxMatch.get ().isContinue ())
aWildcardMatch.get ();
if (aBusdoxExactMatch.get ().isContinue ())
aWildcardBestMatch.get ();
break;
case WILDCARD_THEN_BUSDOX:
if (aWildcardMatch.get ().isContinue ())
aBusdoxMatch.get ();
if (aWildcardBestMatch.get ().isContinue ())
aBusdoxExactMatch.get ();
break;
default:
throw new IllegalStateException ("Unsupported operation mode " + m_eMode);
}
}

/**
* Helper method to find the best match wildcard document type identifier for
* PFUOI 4.3.0. This method only work for peppol-doctype-wildcard scheme.
*
* @param aBaseDocTypes
* The list of document types to filter. Usually this list was obtained
* from an SMP query "get all receiving capabilities of participant".
* May not be <code>null</code>, but maybe empty.
* @param aSearchDocTypeValue
* The document type identifier to search. It may or may not contain
* the Wildcard indicator.
* @param aMatchingDocTypeConsumer
* The consumer to be invoked for the first match only. May not be
* <code>null</code>.
* @return Non-<code>null</code>.
*/
@Nonnull
public static ESuccess findPeppolDoctypeWildcardMatch (@Nonnull final ICommonsList <? extends IDocumentTypeIdentifier> aBaseDocTypes,
@Nonnull @Nonempty final IDocumentTypeIdentifier aSearchDocTypeValue,
@Nonnull final Consumer <? super IDocumentTypeIdentifier> aMatchingDocTypeConsumer)
{
ValueEnforcer.notNull (aBaseDocTypes, "BaseDocTypes");
ValueEnforcer.notNull (aSearchDocTypeValue, "SearchDocTypeValue");
ValueEnforcer.notNull (aSearchDocTypeValue.getValue (), "SearchDocTypeValue.Value");
ValueEnforcer.notNull (aMatchingDocTypeConsumer, "MatchingDocTypeConsumer");

final BiFunction <String, String, IDocumentTypeIdentifier> aFuncCheckExistance = (sScheme, sValue) -> {
if (LOGGER.isDebugEnabled ())
LOGGER.debug ("Checking if document type ID '" +
CIdentifier.getURIEncoded (sScheme, sValue) +
"' is contained");
if (aBaseDocTypes.containsAny (x -> x.hasScheme (sScheme) && x.hasValue (sValue)))
return PeppolIdentifierFactory.INSTANCE.createDocumentTypeIdentifier (sScheme, sValue);
return null;
};

// Split the document type identifier value into pieces (throws
// IllegalArgumentException)
final IPeppolDocumentTypeIdentifierParts aParts = PeppolDocumentTypeIdentifierParts.extractFromString (aSearchDocTypeValue.getValue ());

// Just change the customization ID of the parts
final Function <String, String> aFuncCustIDToDocTypeIDValue = sCustomizationID -> new PeppolDocumentTypeIdentifierParts (aParts.getRootNS (),
aParts.getLocalName (),
sCustomizationID,
aParts.getVersion ()).getAsDocumentTypeIdentifierValue ();

// Find the Customization ID to start (without an optional trailing "*")
String sRemainingCustomizationID = StringHelper.trimEnd (aParts.getCustomizationID (),
PeppolIdentifierHelper.DOCUMENT_TYPE_WILDCARD_INDICATOR);

if (sRemainingCustomizationID.indexOf (PeppolIdentifierHelper.DOCUMENT_TYPE_WILDCARD_INDICATOR) >= 0)
{
// If a Customization still contains a "*" it is invalid - no match
LOGGER.error ("Customization ID contains a forbidden wildcard indicator: '" + sRemainingCustomizationID + "'");
return ESuccess.FAILURE;
}

// Search wildcard exact match (make sure no "*" is contained in the
// CustomizationID)
IDocumentTypeIdentifier aSelectedDocTypeID = aFuncCheckExistance.apply (aSearchDocTypeValue.getScheme (),
aFuncCustIDToDocTypeIDValue.apply (sRemainingCustomizationID));
if (aSelectedDocTypeID != null)
{
if (LOGGER.isDebugEnabled ())
LOGGER.debug ("Found a wildcard exact match: '" + aSelectedDocTypeID.getURIEncoded () + "'");
aMatchingDocTypeConsumer.accept (aSelectedDocTypeID);
return ESuccess.SUCCESS;
}

// Search wildcard best match
aSelectedDocTypeID = aFuncCheckExistance.apply (aSearchDocTypeValue.getScheme (),
aFuncCustIDToDocTypeIDValue.apply (sRemainingCustomizationID +
PeppolIdentifierHelper.DOCUMENT_TYPE_WILDCARD_INDICATOR));
if (aSelectedDocTypeID != null)
{
if (LOGGER.isDebugEnabled ())
LOGGER.debug ("Found a wildcard best match: '" + aSelectedDocTypeID.getURIEncoded () + "'");
aMatchingDocTypeConsumer.accept (aSelectedDocTypeID);
return ESuccess.SUCCESS;
}

while (sRemainingCustomizationID.indexOf (PeppolIdentifierHelper.DOCUMENT_TYPE_WILDCARD_PART_SEPARATOR) >= 0)
{
// Remove last part (after last '@')
sRemainingCustomizationID = sRemainingCustomizationID.substring (0,
sRemainingCustomizationID.lastIndexOf (PeppolIdentifierHelper.DOCUMENT_TYPE_WILDCARD_PART_SEPARATOR));

// Try more corse-grain part
aSelectedDocTypeID = aFuncCheckExistance.apply (aSearchDocTypeValue.getScheme (),
aFuncCustIDToDocTypeIDValue.apply (sRemainingCustomizationID +
PeppolIdentifierHelper.DOCUMENT_TYPE_WILDCARD_INDICATOR));

if (aSelectedDocTypeID != null)
{
if (LOGGER.isDebugEnabled ())
LOGGER.debug ("Found a wildcard best match: '" + aSelectedDocTypeID.getURIEncoded () + "'");
aMatchingDocTypeConsumer.accept (aSelectedDocTypeID);
return ESuccess.SUCCESS;
}
}
return ESuccess.FAILURE;
}

@Override
public String toString ()
{
Expand Down
Loading

0 comments on commit 0277cc5

Please sign in to comment.