@@ -35,6 +35,11 @@ public class StructureRuleCreator {
3535 private static final String ZERO_OR_ONE_DESCRIPTION_FORMAT = "%s shall contain at most one %s" ;
3636 private static final String ZERO_OR_ONE_ERROR_FORMAT = "%s contains more than one %s" ;
3737
38+ private static final String ZERO_OR_ONE_IF_PARENT_IS_GROUPING_DESCRIPTION_FORMAT = "%s, when used as a grouping element, shall contain at most one %s" ;
39+ private static final String ZERO_OR_ONE_IF_PARENT_IS_GROUPING_ERROR_FORMAT = "%s is used as a grouping element, but contains more than one %s" ;
40+ private static final String FORBIDDEN_IF_PARENT_IS_NOT_GROUPING_DESCRIPTION_FORMAT = "%s, when used as a non-grouping element, shall not contain %s" ;
41+ private static final String FORBIDDEN_IF_PARENT_IS_NOT_GROUPING_ERROR_FORMAT = "%s is used as a non-grouping element, but contains %s" ;
42+
3843 private static final String ONE_DESCRIPTION_FORMAT = "%s shall contain exactly one %s" ;
3944 private static final String ONE_ERROR_FORMAT = "%s either doesn't contain or contains more than one %s" ;
4045
@@ -76,16 +81,19 @@ public SortedSet<Rule> generateRules(List<ParsedRelationStructure> relations) {
7681 this .pdfVersion .getIso (), "Annex_L" ));
7782 for (ParsedRelationStructure relation : relations ) {
7883 if (shallProcess (relation )) {
79- RuleData data = getRuleData (relation );
80- if (data == null ) {
84+ List < RuleData > datas = getRuleDatas (relation );
85+ if (datas == null || datas . isEmpty () ) {
8186 System .err .println ("Missing rule for " + relation .getDescriptionString ());
8287 continue ;
8388 }
84- RuleId id = Profiles .ruleIdFromValues (PDFAFlavour .Specification .ISO_32005 ,
85- getClause (relation ), 1 );
86- ErrorDetails error = Profiles .errorFromValues (data .errorMessage , Collections .emptyList ());
87- res .add (Profiles .ruleFromValues (id , data .object , null , StructureTag .getTags (relation ), data .description ,
88- data .test , error , annex_l_reference ));
89+ int testNumber = 1 ;
90+ for (RuleData data : datas ) {
91+ RuleId id = Profiles .ruleIdFromValues (PDFAFlavour .Specification .ISO_32005 ,
92+ getClause (relation ), testNumber ++);
93+ ErrorDetails error = Profiles .errorFromValues (data .errorMessage , Collections .emptyList ());
94+ res .add (Profiles .ruleFromValues (id , data .object , null , StructureTag .getTags (relation ), data .description ,
95+ data .test , error , annex_l_reference ));
96+ }
8997 }
9098 }
9199 return res ;
@@ -252,16 +260,24 @@ private boolean shallProcess(ParsedRelationStructure relation) {
252260 && (TaggedPDFHelper .getPdf17StandardRoleTypes ().contains (child ) || child .equals (HN ) || child .equals (CONTENT_ITEM ));
253261 }
254262
255- private RuleData getRuleData (ParsedRelationStructure rel ) {
263+ private List < RuleData > getRuleDatas (ParsedRelationStructure rel ) {
256264 switch (rel .getRelation ()) {
257265 case FORBIDDEN :
258- return constructForbidden (rel );
266+ return Collections . singletonList ( constructForbidden (rel ) );
259267 case AT_LEAST_ONE :
260- return constructAtLeastOne (rel );
261- case ZERO_OR_ONE :
262- return constructZeroOrOne (rel );
263- case ONE :
264- return constructOne (rel );
268+ return Collections .singletonList (constructAtLeastOne (rel ));
269+ case ZERO_OR_ONE : {
270+ RuleData data = constructZeroOrOne (rel );
271+ return data != null ? Collections .singletonList (data ) : null ;
272+ }
273+ case ZERO_OR_ONE_IF_PARENT_IS_GROUPING :
274+ return constructZeroOrOneIfParentIsGrouping (rel );
275+ case ANY_AMOUNT_IF_PARENT_IS_GROUPING :
276+ return Collections .singletonList (constructForbiddenIfParentIsNotGrouping (rel ));
277+ case ONE : {
278+ RuleData data = constructOne (rel );
279+ return data != null ? Collections .singletonList (data ) : null ;
280+ }
265281 case FORBIDDEN_FOR_NON_GROUPING_CHILD :
266282 case DEPENDS_ON_STRUCTURE :
267283 case RUBY :
@@ -292,6 +308,46 @@ private RuleData constructZeroOrOne(ParsedRelationStructure rel) {
292308 getDescriptionOrErrorMessage (ZERO_OR_ONE_ERROR_FORMAT , parent , child ));
293309 }
294310
311+ private List <RuleData > constructZeroOrOneIfParentIsGrouping (ParsedRelationStructure rel ) {
312+ String child = rel .getChild ();
313+ if (child .equals (CONTENT_ITEM )) {
314+ return null ;
315+ }
316+
317+ String parent = rel .getParent ();
318+ String testObj = getTestObject (parent );
319+
320+ List <RuleData > result = new ArrayList <>();
321+ String childTest = "isGrouping == false || " + constructChildElemAmountPart (child ) + " <= 1" ;
322+ result .add (new RuleData (testObj , childTest ,
323+ getDescriptionOrErrorMessage (ZERO_OR_ONE_IF_PARENT_IS_GROUPING_DESCRIPTION_FORMAT , parent , child ),
324+ getDescriptionOrErrorMessage (ZERO_OR_ONE_IF_PARENT_IS_GROUPING_ERROR_FORMAT , parent , child )));
325+ childTest = "isGrouping == true || " + constructChildElemAmountPart (child ) + " == 0" ;
326+ result .add (new RuleData (testObj , childTest ,
327+ getDescriptionOrErrorMessage (FORBIDDEN_IF_PARENT_IS_NOT_GROUPING_DESCRIPTION_FORMAT , parent , child ),
328+ getDescriptionOrErrorMessage (FORBIDDEN_IF_PARENT_IS_NOT_GROUPING_ERROR_FORMAT , parent , child )));
329+ return result ;
330+ }
331+
332+ private RuleData constructForbiddenIfParentIsNotGrouping (ParsedRelationStructure rel ) {
333+ String parent = rel .getParent ();
334+ String child = rel .getChild ();
335+
336+ String childTest ;
337+ String testObj = getTestObject (parent );
338+ switch (child ) {
339+ case CONTENT_ITEM :
340+ childTest = "isGrouping == true || hasContentItems == false" ;
341+ break ;
342+ default :
343+ childTest = "isGrouping == true || " + constructChildElemAmountPart (child ) + " == 0" ;
344+ }
345+
346+ return new RuleData (testObj , childTest ,
347+ getDescriptionOrErrorMessage (FORBIDDEN_IF_PARENT_IS_NOT_GROUPING_DESCRIPTION_FORMAT , parent , child ),
348+ getDescriptionOrErrorMessage (FORBIDDEN_IF_PARENT_IS_NOT_GROUPING_ERROR_FORMAT , parent , child ));
349+ }
350+
295351 private RuleData constructOne (ParsedRelationStructure rel ) {
296352 String child = rel .getChild ();
297353 if (child .equals (CONTENT_ITEM )) {
0 commit comments