diff --git a/README.md b/README.md
index 77277d72..98496f3d 100644
--- a/README.md
+++ b/README.md
@@ -2,19 +2,17 @@
Encode/decode consent information with the IAB GPP Framework
-https://iabtechlab.com/gpp/
-
### Usage
#### Maven
-The official iabgpp java library is distributed through maven central. Please [search maven central](https://search.maven.org/search?q=g:com.iab.gpp) for the current release version.
+The official iabgpp java library is distributed through maven central. Please [search maven central](https://search.maven.org/search?q=g:com.iabgpp) for the current release version.
#### Decoding
```
- com.iab.gpp
+ com.iabgpp
iabgpp-encoder
VERSION
@@ -25,7 +23,7 @@ import com.iab.gpp.encoder.GppModel;
import com.iab.gpp.encoder.section.TcfEuV2;
import com.iab.gpp.encoder.section.UspV1;
-String gppString = "DBACDMA~CPdBusAPdBusANwAAAENAwCAAAAAAAAAAAAAAAAAAAA.QAAA.IAAA~BbA";
+String gppString = "DBACNYA~CPSG_8APSG_8ANwAAAENAwCAAAAAAAAAAAAAAAAAAAAA.QAAA.IAAA~1YNN";
GppModel gppModel = new GppModel(gppString);
TcfEuV2 tcfEuV2Section = (TcfEuV2)gppModel.getSection(TcfEuV2.NAME);
@@ -41,7 +39,7 @@ Integer uspV1Notice = uspV1Section.getNotice();
```
- com.iab.gpp
+ com.iabgpp
iabgpp-encoder
VERSION
@@ -80,13 +78,13 @@ List respectively. The `iabgpp-extras-jackson` library uses Jackson 2.10.3 to pa
```
- com.iab.gpp
+ com.iabgpp
iabgpp-extras
VERSION
- com.iab.gpp
+ com.iabgpp
iabgpp-extras-jackson
VERSION
@@ -114,3 +112,151 @@ String cmpListContent = "...";
Loader loader = new Loader();
CmpList cmpList = loader.cmpList(cmpListContent);
```
+
+### Fields
+
+|Section Name|Section ID|Field|Data Type/Value|
+|------------|----------|-----|---------------|
+|tcfeuv2|2|Version|6 bit int. Value is 2.|
+|tcfeuv2|2|Created|Datetime. Updated when fields are set|
+|tcfeuv2|2|LastUpdated|Datetime. Updated when fields are set|
+|tcfeuv2|2|CmpId|12 bit int|
+|tcfeuv2|2|CmpVersion|12 bit int|
+|tcfeuv2|2|ConsentScreen|6 bit int|
+|tcfeuv2|2|ConsentLanguage|2 character country code|
+|tcfeuv2|2|VendorListVersion|12 bit int|
+|tcfeuv2|2|PolicyVersion|6 bit int. Value is 2|
+|tcfeuv2|2|IsServiceSpecific|Boolean|
+|tcfeuv2|2|UseNonStandardStacks|Boolean|
+|tcfeuv2|2|SpecialFeatureOptins|Boolean list of size 12|
+|tcfeuv2|2|PurposeConsents|Boolean list of size 24|
+|tcfeuv2|2|PurposeLegitimateInterests|Boolean list of size 24|
+|tcfeuv2|2|PurposeOneTreatment|Boolean|
+|tcfeuv2|2|PublisherCountryCode|2 character country code|
+|tcfeuv2|2|VendorConsents|Integer list of variable size|
+|tcfeuv2|2|VendorLegitimateInterests|Integer list of variable size|
+|tcfeuv2|2|PublisherRestrictions|Integer list of variable size|
+|tcfeuv2|2|PublisherPurposesSegmentType|3 bit int. Value is 3|
+|tcfeuv2|2|PublisherConsents|Boolean list of size 24|
+|tcfeuv2|2|PublisherLegitimateInterests|Boolean list of size 24|
+|tcfeuv2|2|NumCustomPurposes|6 bit int|
+|tcfeuv2|2|PublisherCustomConsents|Boolean list where size is set by the NumCustomPurposes field|
+|tcfeuv2|2|PublisherCustomLegitimateInterests|Boolean list where size is set by the NumCustomPurposes field|
+|tcfeuv2|2|VendorsAllowedSegmentType|3 bit int. Value is 2|
+|tcfeuv2|2|VendorsAllowed|Integer list of variable size|
+|tcfeuv2|2|VendorsDisclosedSegmentType|3 bit int. Value is 1|
+|tcfeuv2|2|VendorsDisclosed|Integer list of variable size|
+|tcfcav1|5|Version|6 bit int. Value is 2.|
+|tcfcav1|5|Created|Datetime. Updated when any fields are set|
+|tcfcav1|5|LastUpdated|Datetime. Updated when any fields are set|
+|tcfcav1|5|CmpId|12 bit int|
+|tcfcav1|5|CmpVersion|12 bit int|
+|tcfcav1|5|ConsentScreen|6 bit int|
+|tcfcav1|5|ConsentLanguage|2 character country code|
+|tcfcav1|5|VendorListVersion|12 bit int|
+|tcfcav1|5|TcfPolicyVersion|6 bit int. Value is 2.|
+|tcfcav1|5|UseNonStandardStacks|Boolean|
+|tcfcav1|5|SpecialFeatureExpressConsent|Boolean list of size 12|
+|tcfcav1|5|PurposesExpressConsent|Boolean list of size 24|
+|tcfcav1|5|PurposesImpliedConsent|Boolean list of size 24|
+|tcfcav1|5|VendorExpressConsent|Integer list of variable size|
+|tcfcav1|5|VendorImpliedConsent|Integer list of variable size|
+|tcfcav1|5|PubRestrictions|RangeEntry list of variable size|
+|tcfcav1|5|PubPurposesSegmentType|3 bit int. Value is 3|
+|tcfcav1|5|PubPurposesExpressConsent|Boolean list of size 24|
+|tcfcav1|5|PubPurposesImpliedConsent|Boolean list of size 24|
+|tcfcav1|5|NumCustomPurposes|6 bit int|
+|tcfcav1|5|CustomPurposesExpressConsent|Boolean list where size is set by the NumCustomPurposes field|
+|tcfcav1|5|CustomPurposesImpliedConsent|Boolean list where size is set by the NumCustomPurposes field|
+|tcfcav1|5|DisclosedVendorsSegmentType|3 bit int. Value is 1|
+|tcfcav1|5|DisclosedVendors|Integer list of variable size|
+|uspv1|6|Version|6 bit int. Value is 1|
+|uspv1|6|Notice|2 bit int|
+|uspv1|6|OptOutSale|2 bit int|
+|uspv1|6|LspaCovered|2 bit int|
+|usnat|7|Version|6 bit int. Value is 1|
+|usnat|7|SharingNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usnat|7|SaleOptOutNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usnat|7|SharingOptOutNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usnat|7|TargetedAdvertisingOptOutNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usnat|7|SensitiveDataProcessingOptOutNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usnat|7|SensitiveDataLimitUseNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usnat|7|SaleOptOut|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usnat|7|SharingOptOut|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usnat|7|TargetedAdvertisingOptOut|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usnat|7|SensitiveDataProcessing|2 bit int list of size 12. 0=Not applicable, 1=Yes, 2=No|
+|usnat|7|KnownChildSensitiveDataConsents|2 bit int list of size 2. 0=Not applicable, 1=Yes, 2=No|
+|usnat|7|PersonalDataConsents|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usnat|7|MspaCoveredTransaction|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usnat|7|MspaOptOutOptionMode|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usnat|7|MspaServiceProviderMode|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usnat|7|GpcSegmentType|2 bit int. Value is 1|
+|usnat|7|GpcSegmentIncluded|Boolean. Default value is true|
+|usnat|7|Gpc|Boolean|
+|usca|8|Version|6 bit int. Value is 1|
+|usca|8|SaleOptOutNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usca|8|SharingOptOutNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usca|8|SensitiveDataLimitUseNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usca|8|SaleOptOut|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usca|8|SharingOptOut|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usca|8|SensitiveDataProcessing|2 bit int list of size 9. 0=Not applicable, 1=Yes, 2=No|
+|usca|8|KnownChildSensitiveDataConsents|2 bit int list of size 2. 0=Not applicable, 1=Yes, 2=No|
+|usca|8|PersonalDataConsents|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usca|8|MspaCoveredTransaction|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usca|8|MspaOptOutOptionMode|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usca|8|MspaServiceProviderMode|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usca|8|GpcSegmentType|2 bit int. Value is 1|
+|usca|8|GpcSegmentIncluded|Boolean. Default value is true|
+|usca|8|Gpc|Boolean|
+|usva|9|Version|6 bit int. Value is 1|
+|usva|9|SharingNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usva|9|SaleOptOutNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usva|9|TargetedAdvertisingOptOutNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usva|9|SaleOptOut|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usva|9|TargetedAdvertisingOptOut|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usva|9|SensitiveDataProcessing|2 bit int list of size 8. 0=Not applicable, 1=Yes, 2=No|
+|usva|9|KnownChildSensitiveDataConsents|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usva|9|MspaCoveredTransaction|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usva|9|MspaOptOutOptionMode|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usva|9|MspaServiceProviderMode|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usco|10|Version|6 bit int. Value is 1|
+|usco|10|SharingNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usco|10|SaleOptOutNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usco|10|TargetedAdvertisingOptOutNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usco|10|SaleOptOut|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usco|10|TargetedAdvertisingOptOut|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usco|10|SensitiveDataProcessing|2 bit int list of size 7. 0=Not applicable, 1=Yes, 2=No|
+|usco|10|KnownChildSensitiveDataConsents|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usco|10|MspaCoveredTransaction|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usco|10|MspaOptOutOptionMode|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usco|10|MspaServiceProviderMode|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usco|10|GpcSegmentType|2 bit int. Value is 1|
+|usco|10|GpcSegmentIncluded|Boolean. Default value is true|
+|usco|10|Gpc|Boolean|
+|usut|11|Version|6 bit int. Value is 1|
+|usut|11|SharingNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usut|11|SaleOptOutNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usut|11|TargetedAdvertisingOptOutNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usut|11|SensitiveDataProcessingOptOutNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usut|11|SaleOptOut|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usut|11|TargetedAdvertisingOptOut|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usut|11|SensitiveDataProcessing|2 bit int list of size 8. 0=Not applicable, 1=Yes, 2=No|
+|usut|11|KnownChildSensitiveDataConsents|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usut|11|MspaCoveredTransaction|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usut|11|MspaOptOutOptionMode|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usut|11|MspaServiceProviderMode|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usct|12|Version|6 bit int. Value is 1|
+|usct|12|SharingNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usct|12|SaleOptOutNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usct|12|TargetedAdvertisingOptOutNotice|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usct|12|SaleOptOut|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usct|12|TargetedAdvertisingOptOut|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usct|12|SensitiveDataProcessing|2 bit int list of size 8. 0=Not applicable, 1=Yes, 2=No|
+|usct|12|KnownChildSensitiveDataConsents|2 bit int list of size 3. 0=Not applicable, 1=Yes, 2=No|
+|usct|12|MspaCoveredTransaction|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usct|12|MspaOptOutOptionMode|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usct|12|MspaServiceProviderMode|2 bit int. 0=Not applicable, 1=Yes, 2=No|
+|usct|12|GpcSegmentType|2 bit int. Value is 1|
+|usct|12|GpcSegmentIncluded|Boolean. Default value is true|
+|usct|12|Gpc|Boolean|
+
diff --git a/iabgpp-encoder/pom.xml b/iabgpp-encoder/pom.xml
index 554e8cd5..f7f25486 100644
--- a/iabgpp-encoder/pom.xml
+++ b/iabgpp-encoder/pom.xml
@@ -7,35 +7,17 @@
com.iabgpp
iabgpp-core
- 3.0.0-SNAPSHOT
+ 3.2.3-SNAPSHOT
- com.iabgpp
iabgpp-encoder
- IAB GPP Java Encoder Library
- 3.0.0-SNAPSHOT
- Encode and decode consent information with the IAB GPP Framework
jar
-
-
- The Apache Software License, Version 2.0
- http://www.apache.org/licenses/LICENSE-2.0.txt
-
-
-
-
- UTF-8
- 1.8
- 1.8
-
-
-
org.junit.jupiter
junit-jupiter-engine
- 5.9.0
+ 5.9.2
test
@@ -43,11 +25,32 @@
- org.apache.maven.plugins
- surefire
- 3.0.0-M7
+ maven-surefire-plugin
+ 2.22.2
+
+
+
+ release
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+
+
+
+
+
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/GppModel.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/GppModel.java
index 02fe98d7..a85a5e25 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/GppModel.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/GppModel.java
@@ -1,44 +1,124 @@
package com.iab.gpp.encoder;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.iab.gpp.encoder.error.DecodingException;
import com.iab.gpp.encoder.error.EncodingException;
+import com.iab.gpp.encoder.error.InvalidFieldException;
+import com.iab.gpp.encoder.field.HeaderV1Field;
import com.iab.gpp.encoder.section.EncodableSection;
import com.iab.gpp.encoder.section.HeaderV1;
-import com.iab.gpp.encoder.section.TcfCaV2;
+import com.iab.gpp.encoder.section.Sections;
+import com.iab.gpp.encoder.section.TcfCaV1;
import com.iab.gpp.encoder.section.TcfEuV2;
+import com.iab.gpp.encoder.section.UsCa;
+import com.iab.gpp.encoder.section.UsCo;
+import com.iab.gpp.encoder.section.UsCt;
+import com.iab.gpp.encoder.section.UsDe;
+import com.iab.gpp.encoder.section.UsFl;
+import com.iab.gpp.encoder.section.UsIa;
+import com.iab.gpp.encoder.section.UsMt;
+import com.iab.gpp.encoder.section.UsNat;
+import com.iab.gpp.encoder.section.UsNe;
+import com.iab.gpp.encoder.section.UsNh;
+import com.iab.gpp.encoder.section.UsNj;
+import com.iab.gpp.encoder.section.UsOr;
+import com.iab.gpp.encoder.section.UsTn;
+import com.iab.gpp.encoder.section.UsTx;
+import com.iab.gpp.encoder.section.UsUt;
+import com.iab.gpp.encoder.section.UsVa;
import com.iab.gpp.encoder.section.UspV1;
public class GppModel {
private Map sections = new HashMap<>();
- private String[] sectionOrder = new String[] {TcfEuV2.NAME, TcfCaV2.NAME, UspV1.NAME};
+
+ private String encodedString;
+
+ private boolean dirty = false;
+ private boolean decoded = true;
public GppModel() {
}
- public GppModel(String encodedString) throws DecodingException {
- if (encodedString != null && encodedString.length() > 0) {
- this.decode(encodedString);
- }
+ public GppModel(String encodedString) {
+ decode(encodedString);
+ }
+
+ public void setFieldValue(int sectionId, String fieldName, Object value) {
+ setFieldValue(Sections.SECTION_ID_NAME_MAP.get(sectionId), fieldName, value);
}
public void setFieldValue(String sectionName, String fieldName, Object value) {
+ if (!this.decoded) {
+ this.sections = this.decodeModel(this.encodedString);
+ this.dirty = false;
+ this.decoded = true;
+ }
+
EncodableSection section = null;
if (!this.sections.containsKey(sectionName)) {
- if (sectionName.equals(TcfCaV2.NAME)) {
- section = new TcfCaV2();
- this.sections.put(TcfCaV2.NAME, section);
+ if (sectionName.equals(TcfCaV1.NAME)) {
+ section = new TcfCaV1();
+ this.sections.put(TcfCaV1.NAME, section);
} else if (sectionName.equals(TcfEuV2.NAME)) {
section = new TcfEuV2();
this.sections.put(TcfEuV2.NAME, section);
} else if (sectionName.equals(UspV1.NAME)) {
section = new UspV1();
this.sections.put(UspV1.NAME, section);
+ } else if (sectionName.equals(UsNat.NAME)) {
+ section = new UsNat();
+ this.sections.put(UsNat.NAME, section);
+ } else if (sectionName.equals(UsCa.NAME)) {
+ section = new UsCa();
+ this.sections.put(UsCa.NAME, section);
+ } else if (sectionName.equals(UsVa.NAME)) {
+ section = new UsVa();
+ this.sections.put(UsVa.NAME, section);
+ } else if (sectionName.equals(UsCo.NAME)) {
+ section = new UsCo();
+ this.sections.put(UsCo.NAME, section);
+ } else if (sectionName.equals(UsUt.NAME)) {
+ section = new UsUt();
+ this.sections.put(UsUt.NAME, section);
+ } else if (sectionName.equals(UsCt.NAME)) {
+ section = new UsCt();
+ this.sections.put(UsCt.NAME, section);
+ } else if (sectionName.equals(UsFl.NAME)) {
+ section = new UsFl();
+ this.sections.put(UsFl.NAME, section);
+ } else if (sectionName.equals(UsMt.NAME)) {
+ section = new UsMt();
+ this.sections.put(UsMt.NAME, section);
+ } else if (sectionName.equals(UsOr.NAME)) {
+ section = new UsOr();
+ this.sections.put(UsOr.NAME, section);
+ } else if (sectionName.equals(UsTx.NAME)) {
+ section = new UsTx();
+ this.sections.put(UsTx.NAME, section);
+ } else if (sectionName.equals(UsDe.NAME)) {
+ section = new UsDe();
+ this.sections.put(UsDe.NAME, section);
+ } else if (sectionName.equals(UsIa.NAME)) {
+ section = new UsIa();
+ this.sections.put(UsIa.NAME, section);
+ } else if (sectionName.equals(UsNe.NAME)) {
+ section = new UsNe();
+ this.sections.put(UsNe.NAME, section);
+ } else if (sectionName.equals(UsNh.NAME)) {
+ section = new UsNh();
+ this.sections.put(UsNh.NAME, section);
+ } else if (sectionName.equals(UsNj.NAME)) {
+ section = new UsNj();
+ this.sections.put(UsNj.NAME, section);
+ } else if (sectionName.equals(UsTn.NAME)) {
+ section = new UsTn();
+ this.sections.put(UsTn.NAME, section);
}
} else {
section = this.sections.get(sectionName);
@@ -46,12 +126,23 @@ public void setFieldValue(String sectionName, String fieldName, Object value) {
if (section != null) {
section.setFieldValue(fieldName, value);
+ this.dirty = true;
} else {
- throw new Error(sectionName + " not found");
+ throw new InvalidFieldException(sectionName + "." + fieldName + " not found");
}
}
+ public Object getFieldValue(int sectionId, String fieldName) {
+ return getFieldValue(Sections.SECTION_ID_NAME_MAP.get(sectionId), fieldName);
+ }
+
public Object getFieldValue(String sectionName, String fieldName) {
+ if (!this.decoded) {
+ this.sections = this.decodeModel(this.encodedString);
+ this.dirty = false;
+ this.decoded = true;
+ }
+
if (this.sections.containsKey(sectionName)) {
return this.sections.get(sectionName).getFieldValue(fieldName);
} else {
@@ -59,7 +150,17 @@ public Object getFieldValue(String sectionName, String fieldName) {
}
}
+ public boolean hasField(int sectionId, String fieldName) {
+ return hasField(Sections.SECTION_ID_NAME_MAP.get(sectionId), fieldName);
+ }
+
public boolean hasField(String sectionName, String fieldName) {
+ if (!this.decoded) {
+ this.sections = this.decodeModel(this.encodedString);
+ this.dirty = false;
+ this.decoded = true;
+ }
+
if (this.sections.containsKey(sectionName)) {
return this.sections.get(sectionName).hasField(fieldName);
} else {
@@ -67,17 +168,47 @@ public boolean hasField(String sectionName, String fieldName) {
}
}
+ public boolean hasSection(int sectionId) {
+ return hasSection(Sections.SECTION_ID_NAME_MAP.get(sectionId));
+ }
+
public boolean hasSection(String sectionName) {
+ if (!this.decoded) {
+ this.sections = this.decodeModel(this.encodedString);
+ this.dirty = false;
+ this.decoded = true;
+ }
+
return this.sections.containsKey(sectionName);
}
public HeaderV1 getHeader() {
+ if (!this.decoded) {
+ this.sections = this.decodeModel(this.encodedString);
+ this.dirty = false;
+ this.decoded = true;
+ }
+
HeaderV1 header = new HeaderV1();
- header.setFieldValue("SectionIds", this.getSectionIds());
+ try {
+ header.setFieldValue("SectionIds", this.getSectionIds());
+ } catch (InvalidFieldException e) {
+
+ }
return header;
}
+ public EncodableSection getSection(int sectionId) {
+ return getSection(Sections.SECTION_ID_NAME_MAP.get(sectionId));
+ }
+
public EncodableSection getSection(String sectionName) {
+ if (!this.decoded) {
+ this.sections = this.decodeModel(this.encodedString);
+ this.dirty = false;
+ this.decoded = true;
+ }
+
if (this.sections.containsKey(sectionName)) {
return this.sections.get(sectionName);
} else {
@@ -85,8 +216,32 @@ public EncodableSection getSection(String sectionName) {
}
}
- public TcfCaV2 getTcfCaV2Section() {
- return (TcfCaV2) getSection(TcfCaV2.NAME);
+ public void deleteSection(int sectionId) {
+ deleteSection(Sections.SECTION_ID_NAME_MAP.get(sectionId));
+ }
+
+ public void deleteSection(String sectionName) {
+ if (!this.decoded) {
+ this.sections = this.decodeModel(this.encodedString);
+ this.dirty = false;
+ this.decoded = true;
+ }
+
+ if (this.sections.containsKey(sectionName)) {
+ this.sections.remove(sectionName);
+ this.dirty = true;
+ }
+ }
+
+ public void clear() {
+ this.sections.clear();
+ this.encodedString = null;
+ this.dirty = false;
+ this.decoded = true;
+ }
+
+ public TcfCaV1 getTcfCaV1Section() {
+ return (TcfCaV1) getSection(TcfCaV1.NAME);
}
public TcfEuV2 getTcfEuV2Section() {
@@ -97,10 +252,80 @@ public UspV1 getUspV1Section() {
return (UspV1) getSection(UspV1.NAME);
}
+ public UsNat getUsNatSection() {
+ return (UsNat) getSection(UsNat.NAME);
+ }
+
+ public UsCa getUsCaSection() {
+ return (UsCa) getSection(UsCa.NAME);
+ }
+
+ public UsVa getUsVaSection() {
+ return (UsVa) getSection(UsVa.NAME);
+ }
+
+ public UsCo getUsCoSection() {
+ return (UsCo) getSection(UsCo.NAME);
+ }
+
+ public UsUt getUsUtSection() {
+ return (UsUt) getSection(UsUt.NAME);
+ }
+
+ public UsCt getUsCtSection() {
+ return (UsCt) getSection(UsCt.NAME);
+ }
+
+ public UsFl getUsFlSection() {
+ return (UsFl) getSection(UsFl.NAME);
+ }
+
+ public UsMt getUsMtSection() {
+ return (UsMt) getSection(UsMt.NAME);
+ }
+
+ public UsOr getUsOrSection() {
+ return (UsOr) getSection(UsOr.NAME);
+ }
+
+ public UsTx getUsTxSection() {
+ return (UsTx) getSection(UsTx.NAME);
+ }
+
+ public UsDe getUsDeSection() {
+ return (UsDe) getSection(UsDe.NAME);
+ }
+
+ public UsIa getUsIaSection() {
+ return (UsIa) getSection(UsIa.NAME);
+ }
+
+ public UsNe getUsNeSection() {
+ return (UsNe) getSection(UsNe.NAME);
+ }
+
+ public UsNh getUsNhSection() {
+ return (UsNh) getSection(UsNh.NAME);
+ }
+
+ public UsNj getUsNjSection() {
+ return (UsNj) getSection(UsNj.NAME);
+ }
+
+ public UsTn getUsTnSection() {
+ return (UsTn) getSection(UsTn.NAME);
+ }
+
public List getSectionIds() {
+ if (!this.decoded) {
+ this.sections = this.decodeModel(this.encodedString);
+ this.dirty = false;
+ this.decoded = true;
+ }
+
List sectionIds = new ArrayList<>();
- for (int i = 0; i < this.sectionOrder.length; i++) {
- String sectionName = this.sectionOrder[i];
+ for (int i = 0; i < Sections.SECTION_ORDER.size(); i++) {
+ String sectionName = Sections.SECTION_ORDER.get(i);
if (this.sections.containsKey(sectionName)) {
EncodableSection section = this.sections.get(sectionName);
sectionIds.add(section.getId());
@@ -109,50 +334,132 @@ public List getSectionIds() {
return sectionIds;
}
- public String encode() throws EncodingException {
+ protected String encodeModel(Map sections) {
List encodedSections = new ArrayList<>();
List sectionIds = new ArrayList<>();
- for (int i = 0; i < this.sectionOrder.length; i++) {
- String sectionName = this.sectionOrder[i];
- if (this.sections.containsKey(sectionName)) {
- EncodableSection section = this.sections.get(sectionName);
+ for (int i = 0; i < Sections.SECTION_ORDER.size(); i++) {
+ String sectionName = Sections.SECTION_ORDER.get(i);
+ if (sections.containsKey(sectionName)) {
+ EncodableSection section = sections.get(sectionName);
encodedSections.add(section.encode());
sectionIds.add(section.getId());
}
}
HeaderV1 header = new HeaderV1();
- header.setFieldValue("SectionIds", this.getSectionIds());
+ try {
+ header.setFieldValue("SectionIds", getSectionIds());
+ } catch (InvalidFieldException e) {
+ throw new EncodingException(e);
+ }
encodedSections.add(0, header.encode());
String encodedString = encodedSections.stream().collect(Collectors.joining("~"));
return encodedString;
}
- public void decode(String str) throws DecodingException {
- this.sections.clear();
+ protected Map decodeModel(String str) {
+ if (str == null || str.isEmpty() || str.startsWith("D")) {
+ Map sections = new HashMap<>();
- String[] encodedSections = str.split("~");
- HeaderV1 header = new HeaderV1(encodedSections[0]);
- this.sections.put(HeaderV1.NAME, header);
+ if (str != null && !str.isEmpty()) {
+ String[] encodedSections = str.split("~");
+ HeaderV1 header = new HeaderV1(encodedSections[0]);
+ sections.put(HeaderV1.NAME, header);
- @SuppressWarnings("unchecked")
- List sectionIds = (List) header.getFieldValue("SectionIds");
- for (int i = 0; i < sectionIds.size(); i++) {
- if (sectionIds.get(i).equals(TcfEuV2.ID)) {
- TcfEuV2 section = new TcfEuV2(encodedSections[i + 1]);
- this.sections.put(TcfEuV2.NAME, section);
- } else if (sectionIds.get(i).equals(TcfCaV2.ID)) {
- TcfCaV2 section = new TcfCaV2(encodedSections[i + 1]);
- this.sections.put(TcfCaV2.NAME, section);
- } else if (sectionIds.get(i).equals(UspV1.ID)) {
- UspV1 section = new UspV1(encodedSections[i + 1]);
- this.sections.put(UspV1.NAME, section);
+ @SuppressWarnings("unchecked")
+ List sectionIds = (List) header.getFieldValue("SectionIds");
+ for (int i = 0; i < sectionIds.size(); i++) {
+ if (sectionIds.get(i).equals(TcfEuV2.ID)) {
+ TcfEuV2 section = new TcfEuV2(encodedSections[i + 1]);
+ sections.put(TcfEuV2.NAME, section);
+ } else if (sectionIds.get(i).equals(TcfCaV1.ID)) {
+ TcfCaV1 section = new TcfCaV1(encodedSections[i + 1]);
+ sections.put(TcfCaV1.NAME, section);
+ } else if (sectionIds.get(i).equals(UspV1.ID)) {
+ UspV1 section = new UspV1(encodedSections[i + 1]);
+ sections.put(UspV1.NAME, section);
+ } else if (sectionIds.get(i).equals(UsCa.ID)) {
+ UsCa section = new UsCa(encodedSections[i + 1]);
+ sections.put(UsCa.NAME, section);
+ } else if (sectionIds.get(i).equals(UsNat.ID)) {
+ UsNat section = new UsNat(encodedSections[i + 1]);
+ sections.put(UsNat.NAME, section);
+ } else if (sectionIds.get(i).equals(UsVa.ID)) {
+ UsVa section = new UsVa(encodedSections[i + 1]);
+ sections.put(UsVa.NAME, section);
+ } else if (sectionIds.get(i).equals(UsCo.ID)) {
+ UsCo section = new UsCo(encodedSections[i + 1]);
+ sections.put(UsCo.NAME, section);
+ } else if (sectionIds.get(i).equals(UsUt.ID)) {
+ UsUt section = new UsUt(encodedSections[i + 1]);
+ sections.put(UsUt.NAME, section);
+ } else if (sectionIds.get(i).equals(UsCt.ID)) {
+ UsCt section = new UsCt(encodedSections[i + 1]);
+ sections.put(UsCt.NAME, section);
+ } else if (sectionIds.get(i).equals(UsFl.ID)) {
+ UsFl section = new UsFl(encodedSections[i + 1]);
+ sections.put(UsFl.NAME, section);
+ } else if (sectionIds.get(i).equals(UsMt.ID)) {
+ UsMt section = new UsMt(encodedSections[i + 1]);
+ sections.put(UsMt.NAME, section);
+ } else if (sectionIds.get(i).equals(UsOr.ID)) {
+ UsOr section = new UsOr(encodedSections[i + 1]);
+ sections.put(UsOr.NAME, section);
+ } else if (sectionIds.get(i).equals(UsTx.ID)) {
+ UsTx section = new UsTx(encodedSections[i + 1]);
+ sections.put(UsTx.NAME, section);
+ } else if (sectionIds.get(i).equals(UsDe.ID)) {
+ UsDe section = new UsDe(encodedSections[i + 1]);
+ sections.put(UsDe.NAME, section);
+ } else if (sectionIds.get(i).equals(UsIa.ID)) {
+ UsIa section = new UsIa(encodedSections[i + 1]);
+ sections.put(UsIa.NAME, section);
+ } else if (sectionIds.get(i).equals(UsNe.ID)) {
+ UsNe section = new UsNe(encodedSections[i + 1]);
+ sections.put(UsNe.NAME, section);
+ } else if (sectionIds.get(i).equals(UsNh.ID)) {
+ UsNh section = new UsNh(encodedSections[i + 1]);
+ sections.put(UsNh.NAME, section);
+ } else if (sectionIds.get(i).equals(UsNj.ID)) {
+ UsNj section = new UsNj(encodedSections[i + 1]);
+ sections.put(UsNj.NAME, section);
+ } else if (sectionIds.get(i).equals(UsTn.ID)) {
+ UsTn section = new UsTn(encodedSections[i + 1]);
+ sections.put(UsTn.NAME, section);
+ }
+ }
}
+
+ return sections;
+ } else if (str.startsWith("C")) {
+ // old tcfeu only string
+ Map sections = new HashMap<>();
+
+ TcfEuV2 section = new TcfEuV2(str);
+ sections.put(TcfEuV2.NAME, section);
+
+ HeaderV1 header = new HeaderV1();
+ header.setFieldValue(HeaderV1Field.SECTION_IDS, Arrays.asList(2));
+ sections.put(HeaderV1.NAME, section);
+
+ return sections;
+ } else {
+ throw new DecodingException("Unable to decode '" + str + "'");
}
}
- public String encodeSection(String sectionName) throws EncodingException {
+ public String encodeSection(int sectionId) {
+ return encodeSection(Sections.SECTION_ID_NAME_MAP.get(sectionId));
+ }
+
+ public String encodeSection(String sectionName) {
+ if (!this.decoded) {
+ this.sections = this.decodeModel(this.encodedString);
+ this.dirty = false;
+ this.decoded = true;
+ }
+
if (this.sections.containsKey(sectionName)) {
return this.sections.get(sectionName).encode();
} else {
@@ -160,18 +467,76 @@ public String encodeSection(String sectionName) throws EncodingException {
}
}
- public void decodeSection(String sectionName, String encodedString) throws DecodingException {
+ public void decodeSection(int sectionId, String encodedString) {
+ decodeSection(Sections.SECTION_ID_NAME_MAP.get(sectionId), encodedString);
+ }
+
+ public void decodeSection(String sectionName, String encodedString) {
+ if (!this.decoded) {
+ this.sections = this.decodeModel(this.encodedString);
+ this.dirty = false;
+ this.decoded = true;
+ }
+
EncodableSection section = null;
if (!this.sections.containsKey(sectionName)) {
if (sectionName.equals(TcfEuV2.NAME)) {
section = new TcfEuV2();
this.sections.put(TcfEuV2.NAME, section);
- } else if (sectionName.equals(TcfCaV2.NAME)) {
- section = new TcfCaV2();
- this.sections.put(TcfCaV2.NAME, section);
+ } else if (sectionName.equals(TcfCaV1.NAME)) {
+ section = new TcfCaV1();
+ this.sections.put(TcfCaV1.NAME, section);
} else if (sectionName.equals(UspV1.NAME)) {
section = new UspV1();
this.sections.put(UspV1.NAME, section);
+ } else if (sectionName.equals(UsNat.NAME)) {
+ section = new UsNat();
+ this.sections.put(UsNat.NAME, section);
+ } else if (sectionName.equals(UsCa.NAME)) {
+ section = new UsCa();
+ this.sections.put(UsCa.NAME, section);
+ } else if (sectionName.equals(UsVa.NAME)) {
+ section = new UsVa();
+ this.sections.put(UsVa.NAME, section);
+ } else if (sectionName.equals(UsCo.NAME)) {
+ section = new UsCo();
+ this.sections.put(UsCo.NAME, section);
+ } else if (sectionName.equals(UsUt.NAME)) {
+ section = new UsUt();
+ this.sections.put(UsUt.NAME, section);
+ } else if (sectionName.equals(UsCt.NAME)) {
+ section = new UsCt();
+ this.sections.put(UsCt.NAME, section);
+ } else if (sectionName.equals(UsFl.NAME)) {
+ section = new UsFl();
+ this.sections.put(UsFl.NAME, section);
+ } else if (sectionName.equals(UsMt.NAME)) {
+ section = new UsMt();
+ this.sections.put(UsMt.NAME, section);
+ } else if (sectionName.equals(UsOr.NAME)) {
+ section = new UsOr();
+ this.sections.put(UsOr.NAME, section);
+ } else if (sectionName.equals(UsTx.NAME)) {
+ section = new UsTx();
+ this.sections.put(UsTx.NAME, section);
+ }else if (sectionName.equals(UsDe.NAME)) {
+ section = new UsDe();
+ this.sections.put(UsDe.NAME, section);
+ }else if (sectionName.equals(UsIa.NAME)) {
+ section = new UsIa();
+ this.sections.put(UsIa.NAME, section);
+ }else if (sectionName.equals(UsNe.NAME)) {
+ section = new UsNe();
+ this.sections.put(UsNe.NAME, section);
+ }else if (sectionName.equals(UsNh.NAME)) {
+ section = new UsNh();
+ this.sections.put(UsNh.NAME, section);
+ }else if (sectionName.equals(UsNj.NAME)) {
+ section = new UsNj();
+ this.sections.put(UsNj.NAME, section);
+ }else if (sectionName.equals(UsTn.NAME)) {
+ section = new UsTn();
+ this.sections.put(UsTn.NAME, section);
}
} else {
section = this.sections.get(sectionName);
@@ -179,6 +544,25 @@ public void decodeSection(String sectionName, String encodedString) throws Decod
if (section != null) {
section.decode(encodedString);
+ this.dirty = true;
+ }
+ }
+
+ public String encode() {
+ if (this.encodedString == null || this.encodedString.isEmpty() || this.dirty) {
+ this.encodedString = encodeModel(this.sections);
+ this.dirty = false;
+ this.decoded = true;
}
+
+ return this.encodedString;
+ }
+
+ public void decode(String encodedString) {
+ this.encodedString = encodedString;
+ this.dirty = false;
+ this.decoded = false;
}
+
+
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/Base64UrlEncoder.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/base64/AbstractBase64UrlEncoder.java
similarity index 84%
rename from iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/Base64UrlEncoder.java
rename to iabgpp-encoder/src/main/java/com/iab/gpp/encoder/base64/AbstractBase64UrlEncoder.java
index a141ecea..a5434073 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/Base64UrlEncoder.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/base64/AbstractBase64UrlEncoder.java
@@ -1,13 +1,17 @@
-package com.iab.gpp.encoder.datatype.encoder;
+package com.iab.gpp.encoder.base64;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import com.iab.gpp.encoder.datatype.encoder.FixedIntegerEncoder;
import com.iab.gpp.encoder.error.DecodingException;
import com.iab.gpp.encoder.error.EncodingException;
-public class Base64UrlEncoder {
+public abstract class AbstractBase64UrlEncoder {
+
+ abstract protected String pad(String bitString);
+
/**
* Base 64 URL character set. Different from standard Base64 char set in that '+' and '/' are
* replaced with '-' and '_'.
@@ -28,19 +32,13 @@ public class Base64UrlEncoder {
private static Pattern BASE64URL_VERIFICATION_PATTERN =
Pattern.compile("^[A-Za-z0-9\\-_]*$", Pattern.CASE_INSENSITIVE);
- public static String encode(String bitString) throws EncodingException {
+ public String encode(String bitString) {
// should only be 0 or 1
if (!BITSTRING_VERIFICATION_PATTERN.matcher(bitString).matches()) {
throw new EncodingException("Unencodable Base64Url '" + bitString + "'");
}
- while (bitString.length() % 8 > 0) {
- bitString += "0";
- }
-
- while (bitString.length() % 6 > 0) {
- bitString += "0";
- }
+ bitString = pad(bitString);
String str = "";
@@ -50,7 +48,7 @@ public static String encode(String bitString) throws EncodingException {
try {
int n = FixedIntegerEncoder.decode(s);
- Character c = Base64UrlEncoder.DICT.charAt(n);
+ Character c = AbstractBase64UrlEncoder.DICT.charAt(n);
str += c;
index += 6;
} catch (DecodingException e) {
@@ -61,7 +59,7 @@ public static String encode(String bitString) throws EncodingException {
return str;
}
- public static String decode(String str) throws DecodingException {
+ public String decode(String str) {
// should contain only characters from the base64url set
if (!BASE64URL_VERIFICATION_PATTERN.matcher(str).matches()) {
throw new DecodingException("Undecodable Base64URL string");
@@ -71,7 +69,7 @@ public static String decode(String str) throws DecodingException {
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
- Integer n = Base64UrlEncoder.REVERSE_DICT.get(c);
+ Integer n = AbstractBase64UrlEncoder.REVERSE_DICT.get(c);
String s = FixedIntegerEncoder.encode(n, 6);
bitString += s;
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/base64/CompressedBase64UrlEncoder.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/base64/CompressedBase64UrlEncoder.java
new file mode 100644
index 00000000..638b4d7d
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/base64/CompressedBase64UrlEncoder.java
@@ -0,0 +1,31 @@
+package com.iab.gpp.encoder.base64;
+
+import java.util.Arrays;
+
+public class CompressedBase64UrlEncoder extends AbstractBase64UrlEncoder {
+
+ private static CompressedBase64UrlEncoder instance = new CompressedBase64UrlEncoder();
+
+ private CompressedBase64UrlEncoder() {
+
+ }
+
+ public static CompressedBase64UrlEncoder getInstance() {
+ return instance;
+ }
+
+ @Override
+ protected String pad(String bitString) {
+ // https://github.com/InteractiveAdvertisingBureau/Global-Privacy-Platform/blob/main/Core/Consent%20String%20Specification.md#creating-a-gpp-string
+ char[] chars1 = null;
+ if(bitString.length() % 6 > 0) {
+ chars1 = new char[6 - (bitString.length() % 6)];
+ } else {
+ chars1 = new char[0];
+ }
+ Arrays.fill(chars1, '0');
+
+ return bitString + new String(chars1);
+ }
+
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/base64/TraditionalBase64UrlEncoder.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/base64/TraditionalBase64UrlEncoder.java
new file mode 100644
index 00000000..78ffe719
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/base64/TraditionalBase64UrlEncoder.java
@@ -0,0 +1,28 @@
+package com.iab.gpp.encoder.base64;
+
+import java.util.Arrays;
+
+public class TraditionalBase64UrlEncoder extends AbstractBase64UrlEncoder {
+
+ private static TraditionalBase64UrlEncoder instance = new TraditionalBase64UrlEncoder();
+
+ private TraditionalBase64UrlEncoder() {
+
+ }
+
+ public static TraditionalBase64UrlEncoder getInstance() {
+ return instance;
+ }
+
+ @Override
+ protected String pad(String bitString) {
+ if(bitString.length() % 24 > 0) {
+ char[] chars = new char[24 - (bitString.length() % 24)];
+ Arrays.fill(chars, '0');
+ return bitString + new String(chars);
+ } else {
+ return bitString;
+ }
+ }
+
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/bitstring/BitStringEncoder.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/bitstring/BitStringEncoder.java
new file mode 100644
index 00000000..12bcb182
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/bitstring/BitStringEncoder.java
@@ -0,0 +1,61 @@
+package com.iab.gpp.encoder.bitstring;
+
+import java.util.List;
+import com.iab.gpp.encoder.datatype.AbstractEncodableBitStringDataType;
+import com.iab.gpp.encoder.datatype.SubstringException;
+import com.iab.gpp.encoder.error.DecodingException;
+import com.iab.gpp.encoder.error.EncodingException;
+import com.iab.gpp.encoder.field.EncodableBitStringFields;
+
+public class BitStringEncoder {
+
+ private static BitStringEncoder instance = new BitStringEncoder();
+
+ private BitStringEncoder() {
+
+ }
+
+ public static BitStringEncoder getInstance() {
+ return instance;
+ }
+
+ public String encode(EncodableBitStringFields fields, List fieldNames) {
+ String bitString = "";
+ for (int i = 0; i < fieldNames.size(); i++) {
+ String fieldName = fieldNames.get(i);
+ if (fields.containsKey(fieldName)) {
+ AbstractEncodableBitStringDataType> field = fields.get(fieldName);
+ bitString += field.encode();
+ } else {
+ throw new EncodingException("Field not found: '" + fieldName + "'");
+ }
+ }
+
+ return bitString;
+ }
+
+ public void decode(String bitString, List fieldNames, EncodableBitStringFields fields) {
+ int index = 0;
+ for (int i = 0; i < fieldNames.size(); i++) {
+ String fieldName = fieldNames.get(i);
+ if (fields.containsKey(fieldName)) {
+ AbstractEncodableBitStringDataType> field = fields.get(fieldName);
+ try {
+ String substring = field.substring(bitString, index);
+ field.decode(substring);
+ index += substring.length();
+ } catch (SubstringException e) {
+ if(field.getHardFailIfMissing()) {
+ throw new DecodingException("Unable to decode " + fieldName, e);
+ } else {
+ return;
+ }
+ } catch (Exception e) {
+ throw new DecodingException("Unable to decode " + fieldName, e);
+ }
+ } else {
+ throw new DecodingException("Field not found: '" + fieldName + "'");
+ }
+ }
+ }
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/AbstractEncodableBitStringDataType.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/AbstractEncodableBitStringDataType.java
index d8c3dcfc..07d9588c 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/AbstractEncodableBitStringDataType.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/AbstractEncodableBitStringDataType.java
@@ -1,19 +1,25 @@
package com.iab.gpp.encoder.datatype;
-import com.iab.gpp.encoder.error.DecodingException;
-import com.iab.gpp.encoder.error.EncodingException;
-
-public abstract class AbstractEncodableBitStringDataType {
+import java.util.Collection;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import com.iab.gpp.encoder.error.ValidationException;
+
+public abstract class AbstractEncodableBitStringDataType implements EncodableDataType {
+ //this if for backwards compatibility with the newer fields
+ protected boolean hardFailIfMissing = true;
+ protected Predicate validator = null;
protected T value;
- public AbstractEncodableBitStringDataType() {
-
+ protected AbstractEncodableBitStringDataType(boolean hardFailIfMissing) {
+ this.hardFailIfMissing = hardFailIfMissing;
}
-
- public AbstractEncodableBitStringDataType(T value) {
- this.value = value;
+
+ public AbstractEncodableBitStringDataType withValidator(Predicate validator) {
+ this.validator = validator;
+ return this;
}
-
+
public boolean hasValue() {
return this.value != null;
}
@@ -24,12 +30,28 @@ public T getValue() {
@SuppressWarnings("unchecked")
public void setValue(Object value) {
- this.value = (T) value;
+ T v = (T) value;
+ if (validator == null || validator.test(v)) {
+ this.value = v;
+ } else {
+ if (v instanceof Collection) {
+ throw new ValidationException("Invalid value '"
+ + ((Collection>) v).stream().map(i -> i.toString()).collect(Collectors.joining(",")) + "'");
+ } else {
+ throw new ValidationException("Invalid value '" + v + "'");
+ }
+ }
+
+ }
+
+ public boolean getHardFailIfMissing() {
+ return this.hardFailIfMissing;
}
- public abstract String encode() throws EncodingException;
+ public abstract String encode();
+
+ public abstract void decode(String bitString);
- public abstract void decode(String bitString) throws DecodingException;
+ public abstract String substring(String bitString, int fromIndex) throws SubstringException;
- public abstract String substring(String bitString, int fromIndex) throws DecodingException;
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/DataType.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/DataType.java
new file mode 100644
index 00000000..8a0d2c07
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/DataType.java
@@ -0,0 +1,7 @@
+package com.iab.gpp.encoder.datatype;
+
+public interface DataType {
+ boolean hasValue();
+ T getValue();
+ void setValue(Object value);
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableArrayOfFixedIntegerRanges.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableArrayOfFixedIntegerRanges.java
new file mode 100644
index 00000000..699e0d26
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableArrayOfFixedIntegerRanges.java
@@ -0,0 +1,110 @@
+package com.iab.gpp.encoder.datatype;
+
+import java.util.ArrayList;
+import java.util.List;
+import com.iab.gpp.encoder.datatype.encoder.FixedIntegerEncoder;
+import com.iab.gpp.encoder.datatype.encoder.FixedIntegerRangeEncoder;
+import com.iab.gpp.encoder.error.DecodingException;
+import com.iab.gpp.encoder.error.EncodingException;
+
+public class EncodableArrayOfFixedIntegerRanges extends AbstractEncodableBitStringDataType> {
+
+ private int keyBitStringLength;
+ private int typeBitStringLength;
+
+ protected EncodableArrayOfFixedIntegerRanges(int keyBitStringLength, int typeBitStringLength) {
+ super(true);
+ this.keyBitStringLength = keyBitStringLength;
+ this.typeBitStringLength = typeBitStringLength;
+ }
+
+ public EncodableArrayOfFixedIntegerRanges(int keyBitStringLength, int typeBitStringLength, List value) {
+ super(true);
+ this.keyBitStringLength = keyBitStringLength;
+ this.typeBitStringLength = typeBitStringLength;
+ setValue(value);
+ }
+
+ public EncodableArrayOfFixedIntegerRanges(int keyBitStringLength, int typeBitStringLength, List value, boolean hardFailIfMissing) {
+ super(hardFailIfMissing);
+ this.keyBitStringLength = keyBitStringLength;
+ this.typeBitStringLength = typeBitStringLength;
+ setValue(value);
+ }
+
+ @Override
+ public String encode() {
+ try {
+ List entries = this.value;
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(FixedIntegerEncoder.encode(entries.size(), 12));
+ for (RangeEntry entry : entries) {
+ sb.append(FixedIntegerEncoder.encode(entry.getKey(), keyBitStringLength))
+ .append(FixedIntegerEncoder.encode(entry.getType(), typeBitStringLength))
+ .append(FixedIntegerRangeEncoder.encode(entry.getIds()));
+ }
+
+ return sb.toString();
+ } catch (Exception e) {
+ throw new EncodingException(e);
+ }
+ }
+
+ @Override
+ public void decode(String bitString) {
+ try {
+ List entries = new ArrayList<>();
+
+ int size = FixedIntegerEncoder.decode(bitString.substring(0, 12));
+ int index = 12;
+ for (int i = 0; i < size; i++) {
+ int key = FixedIntegerEncoder.decode(bitString.substring(index, index + keyBitStringLength));
+ index += keyBitStringLength;
+
+ int type = FixedIntegerEncoder.decode(bitString.substring(index, index + typeBitStringLength));
+ index += typeBitStringLength;
+
+ String substring = new EncodableFixedIntegerRange().substring(bitString, index);
+ List ids = FixedIntegerRangeEncoder.decode(substring);
+ index += substring.length();
+
+ entries.add(new RangeEntry(key, type, ids));
+ }
+
+ this.value = entries;
+ } catch (Exception e) {
+ throw new DecodingException(e);
+ }
+ }
+
+ @Override
+ public String substring(String bitString, int fromIndex) throws SubstringException {
+ try {
+ StringBuilder sb = new StringBuilder();
+ sb.append(bitString.substring(fromIndex, fromIndex + 12));
+
+ int size = FixedIntegerEncoder.decode(sb.toString());
+
+ int index = fromIndex + sb.length();
+ for (int i = 0; i < size; i++) {
+ String keySubstring = bitString.substring(index, index + keyBitStringLength);
+ index += keySubstring.length();
+ sb.append(keySubstring);
+
+ String typeSubstring = bitString.substring(index, index + typeBitStringLength);
+ index += typeSubstring.length();
+ sb.append(typeSubstring);
+
+ String rangeSubstring = new EncodableFixedIntegerRange().substring(bitString, index);
+ index += rangeSubstring.length();
+ sb.append(rangeSubstring);
+ }
+
+ return sb.toString();
+ } catch (Exception e) {
+ throw new SubstringException(e);
+ }
+ }
+
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableBoolean.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableBoolean.java
index bdb29bb1..340be3ab 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableBoolean.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableBoolean.java
@@ -6,24 +6,41 @@
public class EncodableBoolean extends AbstractEncodableBitStringDataType {
- public EncodableBoolean() {
- super();
+ protected EncodableBoolean() {
+ super(true);
}
public EncodableBoolean(Boolean value) {
- super(value);
+ super(true);
+ setValue(value);
}
- public String encode() throws EncodingException {
- return BooleanEncoder.encode(this.value);
+ public EncodableBoolean(Boolean value, boolean hardFailIfMissing) {
+ super(hardFailIfMissing);
+ setValue(value);
}
- public void decode(String bitString) throws DecodingException {
- this.value = BooleanEncoder.decode(bitString);
+ public String encode() {
+ try {
+ return BooleanEncoder.encode(this.value);
+ } catch (Exception e) {
+ throw new EncodingException(e);
+ }
}
- public String substring(String bitString, int fromIndex) {
- // TODO: validate
- return bitString.substring(fromIndex, fromIndex + 1);
+ public void decode(String bitString) {
+ try {
+ this.value = BooleanEncoder.decode(bitString);
+ } catch (Exception e) {
+ throw new DecodingException(e);
+ }
+ }
+
+ public String substring(String bitString, int fromIndex) throws SubstringException {
+ try {
+ return bitString.substring(fromIndex, fromIndex + 1);
+ } catch (Exception e) {
+ throw new SubstringException(e);
+ }
}
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableDataType.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableDataType.java
new file mode 100644
index 00000000..94d416a6
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableDataType.java
@@ -0,0 +1,7 @@
+package com.iab.gpp.encoder.datatype;
+
+public interface EncodableDataType extends DataType {
+ String encode();
+
+ void decode(String str);
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableDatetime.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableDatetime.java
index ad42a95a..a96c46f4 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableDatetime.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableDatetime.java
@@ -3,27 +3,45 @@
import java.time.ZonedDateTime;
import com.iab.gpp.encoder.datatype.encoder.DatetimeEncoder;
import com.iab.gpp.encoder.error.DecodingException;
+import com.iab.gpp.encoder.error.EncodingException;
public class EncodableDatetime extends AbstractEncodableBitStringDataType {
- public EncodableDatetime() {
- super();
+ protected EncodableDatetime() {
+ super(true);
}
public EncodableDatetime(ZonedDateTime value) {
- super(value);
+ super(true);
+ setValue(value);
+ }
+
+ public EncodableDatetime(ZonedDateTime value, boolean hardFailIfMissing) {
+ super(hardFailIfMissing);
+ setValue(value);
}
public String encode() {
- return DatetimeEncoder.encode(this.value);
+ try {
+ return DatetimeEncoder.encode(this.value);
+ } catch (Exception e) {
+ throw new EncodingException(e);
+ }
}
- public void decode(String bitString) throws DecodingException {
- this.value = DatetimeEncoder.decode(bitString);
+ public void decode(String bitString) {
+ try {
+ this.value = DatetimeEncoder.decode(bitString);
+ } catch (Exception e) {
+ throw new DecodingException(e);
+ }
}
- public String substring(String bitString, int fromIndex) {
- // TODO: validate
- return bitString.substring(fromIndex, fromIndex + 36);
+ public String substring(String bitString, int fromIndex) throws SubstringException {
+ try {
+ return bitString.substring(fromIndex, fromIndex + 36);
+ } catch (Exception e) {
+ throw new SubstringException(e);
+ }
}
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFibonacciInteger.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFibonacciInteger.java
index b9d9187e..3da64a15 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFibonacciInteger.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFibonacciInteger.java
@@ -2,31 +2,50 @@
import com.iab.gpp.encoder.datatype.encoder.FibonacciIntegerEncoder;
import com.iab.gpp.encoder.error.DecodingException;
+import com.iab.gpp.encoder.error.EncodingException;
public class EncodableFibonacciInteger extends AbstractEncodableBitStringDataType {
- public EncodableFibonacciInteger() {
- super();
+ protected EncodableFibonacciInteger() {
+ super(true);
}
public EncodableFibonacciInteger(Integer value) {
- super(value);
+ super(true);
+ setValue(value);
+ }
+
+ public EncodableFibonacciInteger(Integer value, boolean hardFailIfMissing) {
+ super(hardFailIfMissing);
+ setValue(value);
}
public String encode() {
- return FibonacciIntegerEncoder.encode(this.value);
+ try {
+ return FibonacciIntegerEncoder.encode(this.value);
+ } catch (Exception e) {
+ throw new EncodingException(e);
+ }
}
- public void decode(String bitString) throws DecodingException {
- this.value = FibonacciIntegerEncoder.decode(bitString);
+ public void decode(String bitString) {
+ try {
+ this.value = FibonacciIntegerEncoder.decode(bitString);
+ } catch (Exception e) {
+ throw new DecodingException(e);
+ }
}
- public String substring(String bitString, int fromIndex) {
- int index = bitString.indexOf("11", fromIndex);
- if (index > 0) {
- return bitString.substring(fromIndex, index + 2);
- } else {
- return bitString;
+ public String substring(String bitString, int fromIndex) throws SubstringException {
+ try {
+ int index = bitString.indexOf("11", fromIndex);
+ if (index > 0) {
+ return bitString.substring(fromIndex, index + 2);
+ } else {
+ return bitString;
+ }
+ } catch (Exception e) {
+ throw new SubstringException(e);
}
}
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFibonacciIntegerRange.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFibonacciIntegerRange.java
index e0ec7c64..53ab2eb2 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFibonacciIntegerRange.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFibonacciIntegerRange.java
@@ -1,39 +1,70 @@
package com.iab.gpp.encoder.datatype;
+import java.util.ArrayList;
import java.util.List;
+import java.util.TreeSet;
import com.iab.gpp.encoder.datatype.encoder.FibonacciIntegerRangeEncoder;
import com.iab.gpp.encoder.datatype.encoder.FixedIntegerEncoder;
import com.iab.gpp.encoder.error.DecodingException;
+import com.iab.gpp.encoder.error.EncodingException;
public class EncodableFibonacciIntegerRange extends AbstractEncodableBitStringDataType> {
- public EncodableFibonacciIntegerRange() {
- super();
+ protected EncodableFibonacciIntegerRange() {
+ super(true);
}
public EncodableFibonacciIntegerRange(List value) {
- super(value);
+ super(true);
+ setValue(value);
}
+ public EncodableFibonacciIntegerRange(List value, boolean hardFailIfMissing) {
+ super(hardFailIfMissing);
+ setValue(value);
+ }
+
public String encode() {
- return FibonacciIntegerRangeEncoder.encode(this.value);
+ try {
+ return FibonacciIntegerRangeEncoder.encode(this.value);
+ } catch (Exception e) {
+ throw new EncodingException(e);
+ }
}
- public void decode(String bitString) throws DecodingException {
- this.value = FibonacciIntegerRangeEncoder.decode(bitString);
+ public void decode(String bitString) {
+ try {
+ this.value = FibonacciIntegerRangeEncoder.decode(bitString);
+ } catch (Exception e) {
+ throw new DecodingException(e);
+ }
}
- public String substring(String bitString, int fromIndex) throws DecodingException {
- // TODO: add some validation
- int count = FixedIntegerEncoder.decode(bitString.substring(fromIndex, fromIndex + 12));
- int index = fromIndex + 12;
- for (int i = 0; i < count; i++) {
- if (bitString.charAt(index) == '1') {
- index = bitString.indexOf("11", bitString.indexOf("11", index + 1) + 2) + 2;
- } else {
- index = bitString.indexOf("11", index + 1) + 2;
+ public String substring(String bitString, int fromIndex) throws SubstringException {
+ try {
+ int count = FixedIntegerEncoder.decode(bitString.substring(fromIndex, fromIndex + 12));
+ int index = fromIndex + 12;
+ for (int i = 0; i < count; i++) {
+ if (bitString.charAt(index) == '1') {
+ index = bitString.indexOf("11", bitString.indexOf("11", index + 1) + 2) + 2;
+ } else {
+ index = bitString.indexOf("11", index + 1) + 2;
+ }
}
+ return bitString.substring(fromIndex, index);
+ } catch (Exception e) {
+ throw new SubstringException(e);
}
- return bitString.substring(fromIndex, index);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void setValue(Object value) {
+ super.setValue(new ArrayList<>(new TreeSet<>((List) value)));
+ }
+
+ @Override
+ public List getValue() {
+ return new ArrayList<>(super.getValue());
}
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedBitfield.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedBitfield.java
index 581d5ff1..5e6890d9 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedBitfield.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedBitfield.java
@@ -1,5 +1,6 @@
package com.iab.gpp.encoder.datatype;
+import java.util.ArrayList;
import java.util.List;
import com.iab.gpp.encoder.datatype.encoder.FixedBitfieldEncoder;
import com.iab.gpp.encoder.error.DecodingException;
@@ -7,28 +8,69 @@
public class EncodableFixedBitfield extends AbstractEncodableBitStringDataType> {
- private int bitStringLength;
+ private int numElements;
- public EncodableFixedBitfield(int bitStringLength) {
- super();
- this.bitStringLength = bitStringLength;
+ protected EncodableFixedBitfield(int numElements) {
+ super(true);
+ this.numElements = numElements;
}
- public EncodableFixedBitfield(int bitStringLength, List value) {
- super(value);
- this.bitStringLength = bitStringLength;
+ protected EncodableFixedBitfield(int numElements, boolean hardFailIfMissing) {
+ super(hardFailIfMissing);
+ this.numElements = numElements;
}
- public String encode() throws EncodingException {
- return FixedBitfieldEncoder.encode(this.value, this.bitStringLength);
+ public EncodableFixedBitfield(List value) {
+ super(true);
+ this.numElements = value.size();
+ setValue(value);
}
- public void decode(String bitString) throws DecodingException {
- this.value = FixedBitfieldEncoder.decode(bitString);
+ public EncodableFixedBitfield(List value, boolean hardFailIfMissing) {
+ super(hardFailIfMissing);
+ this.numElements = value.size();
+ setValue(value);
}
- public String substring(String bitString, int fromIndex) {
- // TODO: validate
- return bitString.substring(fromIndex, fromIndex + this.bitStringLength);
+ public String encode() {
+ try {
+ return FixedBitfieldEncoder.encode(this.value, this.numElements);
+ } catch (Exception e) {
+ throw new EncodingException(e);
+ }
+ }
+
+ public void decode(String bitString) {
+ try {
+ this.value = FixedBitfieldEncoder.decode(bitString);
+ } catch (Exception e) {
+ throw new DecodingException(e);
+ }
+ }
+
+ public String substring(String bitString, int fromIndex) throws SubstringException {
+ try {
+ return bitString.substring(fromIndex, fromIndex + this.numElements);
+ } catch (Exception e) {
+ throw new SubstringException(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void setValue(Object value) {
+ List v = new ArrayList<>((List) value);
+ for (int i = v.size(); i < numElements; i++) {
+ v.add(false);
+ }
+ if (v.size() > numElements) {
+ v = v.subList(0, numElements);
+ }
+ super.setValue(v);
+ }
+
+ @Override
+ public List getValue() {
+ return new ArrayList<>(super.getValue());
}
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedInteger.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedInteger.java
index 90340f29..9fd25fb0 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedInteger.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedInteger.java
@@ -2,31 +2,50 @@
import com.iab.gpp.encoder.datatype.encoder.FixedIntegerEncoder;
import com.iab.gpp.encoder.error.DecodingException;
+import com.iab.gpp.encoder.error.EncodingException;
public class EncodableFixedInteger extends AbstractEncodableBitStringDataType {
private int bitStringLength;
- public EncodableFixedInteger(int bitStringLength) {
- super();
+ protected EncodableFixedInteger(int bitStringLength) {
+ super(true);
this.bitStringLength = bitStringLength;
}
public EncodableFixedInteger(int bitStringLength, Integer value) {
- super(value);
+ super(true);
this.bitStringLength = bitStringLength;
+ setValue(value);
+ }
+
+ public EncodableFixedInteger(int bitStringLength, Integer value, boolean hardFailIfMissing) {
+ super(hardFailIfMissing);
+ this.bitStringLength = bitStringLength;
+ setValue(value);
}
public String encode() {
- return FixedIntegerEncoder.encode(this.value, this.bitStringLength);
+ try {
+ return FixedIntegerEncoder.encode(this.value, this.bitStringLength);
+ } catch (Exception e) {
+ throw new EncodingException(e);
+ }
}
- public void decode(String bitString) throws DecodingException {
- this.value = FixedIntegerEncoder.decode(bitString);
+ public void decode(String bitString) {
+ try {
+ this.value = FixedIntegerEncoder.decode(bitString);
+ } catch (Exception e) {
+ throw new DecodingException(e);
+ }
}
- public String substring(String bitString, int fromIndex) {
- // TODO: validate
- return bitString.substring(fromIndex, fromIndex + this.bitStringLength);
+ public String substring(String bitString, int fromIndex) throws SubstringException {
+ try {
+ return bitString.substring(fromIndex, fromIndex + this.bitStringLength);
+ } catch (Exception e) {
+ throw new SubstringException(e);
+ }
}
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedIntegerList.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedIntegerList.java
new file mode 100644
index 00000000..f491e5ee
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedIntegerList.java
@@ -0,0 +1,75 @@
+package com.iab.gpp.encoder.datatype;
+
+import java.util.ArrayList;
+import java.util.List;
+import com.iab.gpp.encoder.datatype.encoder.FixedIntegerListEncoder;
+import com.iab.gpp.encoder.error.DecodingException;
+import com.iab.gpp.encoder.error.EncodingException;
+
+public class EncodableFixedIntegerList extends AbstractEncodableBitStringDataType> {
+
+ private int elementBitStringLength;
+ private int numElements;
+
+ protected EncodableFixedIntegerList(int elementBitStringLength, int numElements) {
+ super(true);
+ this.elementBitStringLength = elementBitStringLength;
+ this.numElements = numElements;
+ }
+
+ public EncodableFixedIntegerList(int elementBitStringLength, List value) {
+ super(true);
+ this.elementBitStringLength = elementBitStringLength;
+ this.numElements = value.size();
+ setValue(value);
+ }
+
+ public EncodableFixedIntegerList(int elementBitStringLength, List value, boolean hardFailIfMissing) {
+ super(hardFailIfMissing);
+ this.elementBitStringLength = elementBitStringLength;
+ this.numElements = value.size();
+ setValue(value);
+ }
+
+ public String encode() {
+ try {
+ return FixedIntegerListEncoder.encode(this.value, this.elementBitStringLength, this.numElements);
+ } catch (Exception e) {
+ throw new EncodingException(e);
+ }
+ }
+
+ public void decode(String bitString) {
+ try {
+ this.value = FixedIntegerListEncoder.decode(bitString, this.elementBitStringLength, this.numElements);
+ } catch (Exception e) {
+ throw new DecodingException(e);
+ }
+ }
+
+ public String substring(String bitString, int fromIndex) throws SubstringException {
+ try {
+ return bitString.substring(fromIndex, fromIndex + (this.elementBitStringLength * numElements));
+ } catch (Exception e) {
+ throw new SubstringException(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void setValue(Object value) {
+ List v = new ArrayList<>((List) value);
+ for (int i = v.size(); i < numElements; i++) {
+ v.add(0);
+ }
+ if (v.size() > numElements) {
+ v = v.subList(0, numElements);
+ }
+ super.setValue(v);
+ }
+
+ @Override
+ public List getValue() {
+ return new ArrayList<>(super.getValue());
+ }
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedIntegerRange.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedIntegerRange.java
index 84409d9c..7fdbd5a8 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedIntegerRange.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedIntegerRange.java
@@ -1,40 +1,70 @@
package com.iab.gpp.encoder.datatype;
+import java.util.ArrayList;
import java.util.List;
+import java.util.TreeSet;
import com.iab.gpp.encoder.datatype.encoder.FixedIntegerEncoder;
import com.iab.gpp.encoder.datatype.encoder.FixedIntegerRangeEncoder;
import com.iab.gpp.encoder.error.DecodingException;
+import com.iab.gpp.encoder.error.EncodingException;
public class EncodableFixedIntegerRange extends AbstractEncodableBitStringDataType> {
- public EncodableFixedIntegerRange() {
- super();
+ protected EncodableFixedIntegerRange() {
+ super(true);
}
public EncodableFixedIntegerRange(List value) {
- super(value);
+ super(true);
+ setValue(value);
+ }
+
+ public EncodableFixedIntegerRange(List value, boolean hardFailIfMissing) {
+ super(hardFailIfMissing);
+ setValue(value);
}
public String encode() {
- return FixedIntegerRangeEncoder.encode(this.value);
+ try {
+ return FixedIntegerRangeEncoder.encode(this.value);
+ } catch (Exception e) {
+ throw new EncodingException(e);
+ }
}
- public void decode(String bitString) throws DecodingException {
- this.value = FixedIntegerRangeEncoder.decode(bitString);
+ public void decode(String bitString) {
+ try {
+ this.value = FixedIntegerRangeEncoder.decode(bitString);
+ } catch (Exception e) {
+ throw new DecodingException(e);
+ }
}
- public String substring(String bitString, int fromIndex) throws DecodingException {
- // TODO: add some validation
- int count = FixedIntegerEncoder.decode(bitString.substring(fromIndex, fromIndex + 12));
- int index = fromIndex + 12;
- for (int i = 0; i < count; i++) {
- if (bitString.charAt(index) == '1') {
- index += 33;
- } else {
- index += 17;
+ public String substring(String bitString, int fromIndex) throws SubstringException {
+ try {
+ int count = FixedIntegerEncoder.decode(bitString.substring(fromIndex, fromIndex + 12));
+ int index = fromIndex + 12;
+ for (int i = 0; i < count; i++) {
+ if (bitString.charAt(index) == '1') {
+ index += 33;
+ } else {
+ index += 17;
+ }
}
+ return bitString.substring(fromIndex, index);
+ } catch (Exception e) {
+ throw new SubstringException(e);
}
- return bitString.substring(fromIndex, index);
}
+ @SuppressWarnings("unchecked")
+ @Override
+ public void setValue(Object value) {
+ super.setValue(new ArrayList<>(new TreeSet<>((List) value)));
+ }
+
+ @Override
+ public List getValue() {
+ return new ArrayList<>(super.getValue());
+ }
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedString.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedString.java
index 8ae8035b..4cea9b48 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedString.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableFixedString.java
@@ -8,26 +8,44 @@ public class EncodableFixedString extends AbstractEncodableBitStringDataType value) {
- super(value);
+ super(true);
this.getLengthSupplier = getLengthSupplier;
+ this.setValue(value);
}
- public String encode() throws EncodingException {
- return FixedBitfieldEncoder.encode(this.value, this.getLengthSupplier.getAsInt());
+ public EncodableFlexibleBitfield(IntSupplier getLengthSupplier, List value, boolean hardFailIfMissing) {
+ super(hardFailIfMissing);
+ this.getLengthSupplier = getLengthSupplier;
+ this.setValue(value);
+ }
+
+ public String encode() {
+ try {
+ return FixedBitfieldEncoder.encode(this.value, this.getLengthSupplier.getAsInt());
+ } catch (Exception e) {
+ throw new EncodingException(e);
+ }
+ }
+
+ public void decode(String bitString) {
+ try {
+ this.value = FixedBitfieldEncoder.decode(bitString);
+ } catch (Exception e) {
+ throw new DecodingException(e);
+ }
+ }
+
+ public String substring(String bitString, int fromIndex) throws SubstringException {
+ try {
+ return bitString.substring(fromIndex, fromIndex + this.getLengthSupplier.getAsInt());
+ } catch (Exception e) {
+ throw new SubstringException(e);
+ }
}
- public void decode(String bitString) throws DecodingException {
- this.value = FixedBitfieldEncoder.decode(bitString);
+ @SuppressWarnings("unchecked")
+ @Override
+ public void setValue(Object value) {
+ int numElements = this.getLengthSupplier.getAsInt();
+ List v = new ArrayList<>((List) value);
+ for (int i = v.size(); i < numElements; i++) {
+ v.add(false);
+ }
+ if (v.size() > numElements) {
+ v = v.subList(0, numElements);
+ }
+ super.setValue(v);
}
- public String substring(String bitString, int fromIndex) {
- // TODO: validate
- return bitString.substring(fromIndex, fromIndex + this.getLengthSupplier.getAsInt());
+ @Override
+ public List getValue() {
+ return new ArrayList<>(super.getValue());
}
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableOptimizedFibonacciRange.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableOptimizedFibonacciRange.java
index 43dcf112..2e49855f 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableOptimizedFibonacciRange.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableOptimizedFibonacciRange.java
@@ -2,69 +2,66 @@
import java.util.ArrayList;
import java.util.List;
-import com.iab.gpp.encoder.datatype.encoder.FibonacciIntegerRangeEncoder;
-import com.iab.gpp.encoder.datatype.encoder.FixedBitfieldEncoder;
+import java.util.TreeSet;
import com.iab.gpp.encoder.datatype.encoder.FixedIntegerEncoder;
+import com.iab.gpp.encoder.datatype.encoder.OptimizedFibonacciRangeEncoder;
import com.iab.gpp.encoder.error.DecodingException;
import com.iab.gpp.encoder.error.EncodingException;
public class EncodableOptimizedFibonacciRange extends AbstractEncodableBitStringDataType> {
- public EncodableOptimizedFibonacciRange() {
- super();
+ protected EncodableOptimizedFibonacciRange() {
+ super(true);
}
public EncodableOptimizedFibonacciRange(List value) {
- super(value);
+ super(true);
+ setValue(value);
}
- public String encode() throws EncodingException {
- // TODO: encoding the range before choosing the shortest is inefficient. There is probably a way
- // to identify in advance which will be shorter based on the array length and values
- int max = this.value.size() > 0 ? this.value.get(this.value.size() - 1) : 0;
- String rangeBitString = FibonacciIntegerRangeEncoder.encode(this.value);
- int rangeLength = rangeBitString.length();
- int bitFieldLength = max;
+ public EncodableOptimizedFibonacciRange(List value, boolean hardFailIfMissing) {
+ super(hardFailIfMissing);
+ setValue(value);
+ }
- if (rangeLength <= bitFieldLength) {
- return "1" + FixedIntegerEncoder.encode(max, 16) + rangeBitString;
- } else {
- List bits = new ArrayList<>();
- int index = 0;
- for (int i = 0; i < max; i++) {
- if (i == this.value.get(index) - 1) {
- bits.add(true);
- index++;
- } else {
- bits.add(false);
- }
- }
- return "0" + FixedIntegerEncoder.encode(max, 16) + FixedBitfieldEncoder.encode(bits, bitFieldLength);
+ public String encode() {
+ try {
+ return OptimizedFibonacciRangeEncoder.encode(this.value);
+ } catch (Exception e) {
+ throw new EncodingException(e);
}
}
- public void decode(String bitString) throws DecodingException {
- if (bitString.charAt(16) == '1') {
- this.value = FibonacciIntegerRangeEncoder.decode(bitString.substring(17));
- } else {
- List value = new ArrayList<>();
- List bits = FixedBitfieldEncoder.decode(bitString.substring(17));
- for (int i = 0; i < bits.size(); i++) {
- if (bits.get(i) == true) {
- value.add(i + 1);
- }
- }
- this.value = value;
+ public void decode(String bitString) {
+ try {
+ this.value = OptimizedFibonacciRangeEncoder.decode(bitString);
+ } catch (Exception e) {
+ throw new DecodingException(e);
}
}
- public String substring(String bitString, int fromIndex) throws DecodingException {
- int max = FixedIntegerEncoder.decode(bitString.substring(fromIndex, fromIndex + 16));
- if (bitString.charAt(fromIndex + 16) == '1') {
- return (bitString.substring(fromIndex, 17)
- + new EncodableFibonacciIntegerRange().substring(bitString, fromIndex + 17));
- } else {
- return bitString.substring(fromIndex, fromIndex + 17 + max);
+ public String substring(String bitString, int fromIndex) throws SubstringException {
+ try {
+ int max = FixedIntegerEncoder.decode(bitString.substring(fromIndex, fromIndex + 16));
+ if (bitString.charAt(fromIndex + 16) == '1') {
+ return (bitString.substring(fromIndex, fromIndex + 17)
+ + new EncodableFibonacciIntegerRange().substring(bitString, fromIndex + 17));
+ } else {
+ return bitString.substring(fromIndex, fromIndex + 17 + max);
+ }
+ } catch (Exception e) {
+ throw new SubstringException(e);
}
}
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void setValue(Object value) {
+ super.setValue(new ArrayList<>(new TreeSet<>((List) value)));
+ }
+
+ @Override
+ public List getValue() {
+ return new ArrayList<>(super.getValue());
+ }
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableOptimizedFixedRange.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableOptimizedFixedRange.java
index 0fb2b7d3..57f4b8e9 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableOptimizedFixedRange.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/EncodableOptimizedFixedRange.java
@@ -2,68 +2,67 @@
import java.util.ArrayList;
import java.util.List;
-import com.iab.gpp.encoder.datatype.encoder.FixedBitfieldEncoder;
+import java.util.TreeSet;
import com.iab.gpp.encoder.datatype.encoder.FixedIntegerEncoder;
-import com.iab.gpp.encoder.datatype.encoder.FixedIntegerRangeEncoder;
+import com.iab.gpp.encoder.datatype.encoder.OptimizedFixedRangeEncoder;
import com.iab.gpp.encoder.error.DecodingException;
import com.iab.gpp.encoder.error.EncodingException;
+
public class EncodableOptimizedFixedRange extends AbstractEncodableBitStringDataType> {
- public EncodableOptimizedFixedRange() {
- super();
+ protected EncodableOptimizedFixedRange() {
+ super(true);
}
public EncodableOptimizedFixedRange(List value) {
- super(value);
+ super(true);
+ setValue(value);
}
- public String encode() throws EncodingException {
- // TODO: encoding the range before choosing the shortest is inefficient. There is probably a way
- // to identify in advance which will be shorter based on the array length and values
- int max = this.value.size() > 0 ? this.value.get(this.value.size() - 1) : 0;
- String rangeBitString = FixedIntegerRangeEncoder.encode(this.value);
- int rangeLength = rangeBitString.length();
- int bitFieldLength = max;
+ public EncodableOptimizedFixedRange(List value, boolean hardFailIfMissing) {
+ super(hardFailIfMissing);
+ setValue(value);
+ }
- if (rangeLength <= bitFieldLength) {
- return "1" + FixedIntegerEncoder.encode(max, 16) + rangeBitString;
- } else {
- List bits = new ArrayList<>();
- int index = 0;
- for (int i = 0; i < max; i++) {
- if (i == this.value.get(index) - 1) {
- bits.add(true);
- index++;
- } else {
- bits.add(false);
- }
- }
- return "0" + FixedIntegerEncoder.encode(max, 16) + FixedBitfieldEncoder.encode(bits, bitFieldLength);
+ public String encode() {
+ try {
+ return OptimizedFixedRangeEncoder.encode(this.value);
+ } catch (Exception e) {
+ throw new EncodingException(e);
}
}
- public void decode(String bitString) throws DecodingException {
- if (bitString.charAt(16) == '1') {
- this.value = FixedIntegerRangeEncoder.decode(bitString.substring(17));
- } else {
- List value = new ArrayList<>();
- List bits = FixedBitfieldEncoder.decode(bitString.substring(17));
- for (int i = 0; i < bits.size(); i++) {
- if (bits.get(i) == true) {
- value.add(i + 1);
- }
- }
- this.value = value;
+ public void decode(String bitString) {
+ try {
+ this.value = OptimizedFixedRangeEncoder.decode(bitString);
+ } catch (Exception e) {
+ throw new DecodingException(e);
}
}
- public String substring(String bitString, int fromIndex) throws DecodingException {
- int max = FixedIntegerEncoder.decode(bitString.substring(fromIndex, fromIndex + 16));
- if (bitString.charAt(fromIndex + 16) == '1') {
- return bitString.substring(fromIndex, 17) + new EncodableFixedIntegerRange().substring(bitString, fromIndex + 17);
- } else {
- return bitString.substring(fromIndex, fromIndex + 17 + max);
+ public String substring(String bitString, int fromIndex) throws SubstringException {
+ try {
+ int max = FixedIntegerEncoder.decode(bitString.substring(fromIndex, fromIndex + 16));
+ if (bitString.charAt(fromIndex + 16) == '1') {
+ return bitString.substring(fromIndex, fromIndex + 17)
+ + new EncodableFixedIntegerRange().substring(bitString, fromIndex + 17);
+ } else {
+ return bitString.substring(fromIndex, fromIndex + 17 + max);
+ }
+ } catch (Exception e) {
+ throw new SubstringException(e);
}
}
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void setValue(Object value) {
+ super.setValue(new ArrayList<>(new TreeSet<>((List) value)));
+ }
+
+ @Override
+ public List getValue() {
+ return new ArrayList<>(super.getValue());
+ }
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/RangeEntry.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/RangeEntry.java
new file mode 100644
index 00000000..0237ed18
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/RangeEntry.java
@@ -0,0 +1,42 @@
+package com.iab.gpp.encoder.datatype;
+
+import java.util.List;
+
+public class RangeEntry {
+
+ private int key;
+ private int type;
+ private List ids;
+
+ public RangeEntry(int key, int type, List ids) {
+ super();
+ this.key = key;
+ this.type = type;
+ this.ids = ids;
+ }
+
+ public int getKey() {
+ return key;
+ }
+
+ public void setKey(int key) {
+ this.key = key;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ public List getIds() {
+ return ids;
+ }
+
+ public void setIds(List ids) {
+ this.ids = ids;
+ }
+
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/SubstringException.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/SubstringException.java
new file mode 100644
index 00000000..e9df8ca6
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/SubstringException.java
@@ -0,0 +1,18 @@
+package com.iab.gpp.encoder.datatype;
+
+public class SubstringException extends Exception {
+
+ private static final long serialVersionUID = 1825100490468259890L;
+
+ public SubstringException(String msg) {
+ super(msg);
+ }
+
+ public SubstringException(Exception e) {
+ super(e);
+ }
+
+ public SubstringException(String msg, Exception e) {
+ super(msg, e);
+ }
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/UnencodableCharacter.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/UnencodableCharacter.java
new file mode 100644
index 00000000..4ef9c256
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/UnencodableCharacter.java
@@ -0,0 +1,49 @@
+package com.iab.gpp.encoder.datatype;
+
+import java.util.function.Predicate;
+import com.iab.gpp.encoder.error.ValidationException;
+
+public class UnencodableCharacter implements DataType {
+
+ private Predicate validator;
+ private Character value = null;
+
+ public UnencodableCharacter() {
+ this.validator = v -> true;
+ }
+
+ public UnencodableCharacter(Character value) {
+ this.validator = v -> true;
+ setValue(value);
+ }
+
+ public UnencodableCharacter(Character value, Predicate validator) {
+ this.validator = validator;
+ setValue(value);
+ }
+
+ public void setValidator(Predicate validator) {
+ this.validator = validator;
+ }
+
+ @Override
+ public boolean hasValue() {
+ return this.value != null;
+ }
+
+ @Override
+ public Character getValue() {
+ return this.value;
+ }
+
+ @Override
+ public void setValue(Object value) {
+ Character c = (Character)value.toString().charAt(0);
+ if(validator.test(c)) {
+ this.value = c;
+ } else {
+ throw new ValidationException("Invalid value '" + c + "'");
+ }
+ }
+
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/UnencodableInteger.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/UnencodableInteger.java
new file mode 100644
index 00000000..60dcddfd
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/UnencodableInteger.java
@@ -0,0 +1,49 @@
+package com.iab.gpp.encoder.datatype;
+
+import java.util.function.Predicate;
+import com.iab.gpp.encoder.error.ValidationException;
+
+public class UnencodableInteger implements DataType {
+
+ private Predicate validator;
+ private Integer value = null;
+
+ public UnencodableInteger() {
+ this.validator = v -> true;
+ }
+
+ public UnencodableInteger(Integer value) {
+ this.validator = v -> true;
+ setValue(value);
+ }
+
+ public UnencodableInteger(Integer value, Predicate validator) {
+ this.validator = validator;
+ setValue(value);
+ }
+
+ public void setValidator(Predicate validator) {
+ this.validator = validator;
+ }
+
+ @Override
+ public boolean hasValue() {
+ return this.value != null;
+ }
+
+ @Override
+ public Integer getValue() {
+ return this.value;
+ }
+
+ @Override
+ public void setValue(Object value) {
+ Integer i = (Integer)value;
+ if(validator.test(i)) {
+ this.value = i;
+ } else {
+ throw new ValidationException("Invalid value '" + i + "'");
+ }
+ }
+
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/ArrayOfRangesEntryEncoder.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/ArrayOfRangesEntryEncoder.java
new file mode 100644
index 00000000..de1449b3
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/ArrayOfRangesEntryEncoder.java
@@ -0,0 +1,43 @@
+package com.iab.gpp.encoder.datatype.encoder;
+
+import java.util.regex.Pattern;
+import com.iab.gpp.encoder.error.DecodingException;
+
+public class ArrayOfRangesEntryEncoder {
+
+ private static Pattern BITSTRING_VERIFICATION_PATTERN = Pattern.compile("^[0-1]*$", Pattern.CASE_INSENSITIVE);
+
+ public static String encode(long value, int bitStringLength) {
+ String bitString = "";
+ while (value > 0) {
+ if ((value & 1) == 1) {
+ bitString = "1" + bitString;
+ } else {
+ bitString = "0" + bitString;
+ }
+ value = value >> 1;
+ }
+
+ while (bitString.length() < bitStringLength) {
+ bitString = "0" + bitString;
+ }
+
+ return bitString;
+ }
+
+ public static long decode(String bitString) throws DecodingException {
+ if (!BITSTRING_VERIFICATION_PATTERN.matcher(bitString).matches()) {
+ throw new DecodingException("Undecodable FixedLong '" + bitString + "'");
+ }
+
+ long value = 0;
+
+ for (int i = 0; i < bitString.length(); i++) {
+ if (bitString.charAt(bitString.length() - (i + 1)) == '1') {
+ value += 1L << i;
+ }
+ }
+
+ return value;
+ }
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/BooleanEncoder.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/BooleanEncoder.java
index b49ff391..20e6cbd0 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/BooleanEncoder.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/BooleanEncoder.java
@@ -4,7 +4,7 @@
import com.iab.gpp.encoder.error.EncodingException;
public class BooleanEncoder {
- public static String encode(Boolean value) throws EncodingException {
+ public static String encode(Boolean value) {
if (value == true) {
return "1";
} else if (value == false) {
@@ -14,7 +14,7 @@ public static String encode(Boolean value) throws EncodingException {
}
}
- public static boolean decode(String bitString) throws DecodingException {
+ public static boolean decode(String bitString) {
if (bitString.equals("1")) {
return true;
} else if (bitString.equals("0")) {
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedBitfieldEncoder.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedBitfieldEncoder.java
index 20a71c13..6c922263 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedBitfieldEncoder.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedBitfieldEncoder.java
@@ -10,7 +10,11 @@ public class FixedBitfieldEncoder {
private static Pattern BITSTRING_VERIFICATION_PATTERN = Pattern.compile("^[0-1]*$", Pattern.CASE_INSENSITIVE);
- public static String encode(List value, int bitStringLength) throws EncodingException {
+ public static String encode(List value, int bitStringLength) {
+ if (value.size() > bitStringLength) {
+ throw new EncodingException("Too many values '" + value.size() + "'");
+ }
+
String bitString = "";
for (int i = 0; i < value.size(); i++) {
bitString += BooleanEncoder.encode(value.get(i));
@@ -23,7 +27,7 @@ public static String encode(List value, int bitStringLength) throws Enc
return bitString;
}
- public static List decode(String bitString) throws DecodingException {
+ public static List decode(String bitString) {
if (!BITSTRING_VERIFICATION_PATTERN.matcher(bitString).matches()) {
throw new DecodingException("Undecodable FixedBitfield '" + bitString + "'");
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedIntegerEncoder.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedIntegerEncoder.java
index 2870af60..dfc6802f 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedIntegerEncoder.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedIntegerEncoder.java
@@ -2,6 +2,7 @@
import java.util.regex.Pattern;
import com.iab.gpp.encoder.error.DecodingException;
+import com.iab.gpp.encoder.error.EncodingException;
public class FixedIntegerEncoder {
@@ -20,6 +21,11 @@ public static String encode(int value, int bitStringLength) {
value = value >> 1;
}
+ if (bitString.length() > bitStringLength) {
+ throw new EncodingException(
+ "Numeric value '" + value + "' is too large for a bit string length of '" + bitStringLength + "'");
+ }
+
while (bitString.length() < bitStringLength) {
bitString = "0" + bitString;
}
@@ -31,9 +37,6 @@ public static int decode(String bitString) throws DecodingException {
if (!BITSTRING_VERIFICATION_PATTERN.matcher(bitString).matches()) {
throw new DecodingException("Undecodable FixedInteger '" + bitString + "'");
}
-
- // return parseInt(bitString, 2);
-
int value = 0;
for (int i = 0; i < bitString.length(); i++) {
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedIntegerListEncoder.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedIntegerListEncoder.java
new file mode 100644
index 00000000..044dfe26
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedIntegerListEncoder.java
@@ -0,0 +1,63 @@
+package com.iab.gpp.encoder.datatype.encoder;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+import com.iab.gpp.encoder.error.DecodingException;
+import com.iab.gpp.encoder.error.EncodingException;
+
+public class FixedIntegerListEncoder {
+
+ private static Pattern BITSTRING_VERIFICATION_PATTERN = Pattern.compile("^[0-1]*$", Pattern.CASE_INSENSITIVE);
+
+ public static String encode(List value, int elementBitStringLength, int numElements) {
+ if(value.size() > numElements) {
+ throw new EncodingException("Too many values '" + value.size() + "'");
+ }
+
+ String bitString = "";
+ for (int i = 0; i < value.size(); i++) {
+ bitString += FixedIntegerEncoder.encode(value.get(i), elementBitStringLength);
+ }
+
+ while (bitString.length() < elementBitStringLength * numElements) {
+ bitString += "0";
+ }
+
+ return bitString;
+ }
+
+ public static List decode(String bitString, int elementBitStringLength, int numElements)
+ throws DecodingException {
+ if (!BITSTRING_VERIFICATION_PATTERN.matcher(bitString).matches()) {
+ throw new DecodingException("Undecodable FixedIntegerList '" + bitString + "'");
+ }
+
+ if (bitString.length() > elementBitStringLength * numElements) {
+ throw new DecodingException("Undecodable FixedIntegerList '" + bitString + "'");
+ }
+
+ if (bitString.length() % elementBitStringLength != 0) {
+ throw new DecodingException("Undecodable FixedIntegerList '" + bitString + "'");
+ }
+
+ while (bitString.length() < elementBitStringLength * numElements) {
+ bitString += "0";
+ }
+
+ if (bitString.length() > elementBitStringLength * numElements) {
+ bitString = bitString.substring(0, elementBitStringLength * numElements);
+ }
+
+ List value = new ArrayList<>();
+ for (int i = 0; i < bitString.length(); i += elementBitStringLength) {
+ value.add(FixedIntegerEncoder.decode(bitString.substring(i, i + elementBitStringLength)));
+ }
+
+ while (value.size() < numElements) {
+ value.add(0);
+ }
+
+ return value;
+ }
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedLongEncoder.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedLongEncoder.java
index 76ef8baf..c244a601 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedLongEncoder.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedLongEncoder.java
@@ -2,6 +2,7 @@
import java.util.regex.Pattern;
import com.iab.gpp.encoder.error.DecodingException;
+import com.iab.gpp.encoder.error.EncodingException;
public class FixedLongEncoder {
@@ -18,6 +19,11 @@ public static String encode(long value, int bitStringLength) {
value = value >> 1;
}
+ if (bitString.length() > bitStringLength) {
+ throw new EncodingException(
+ "Numeric value '" + value + "' is too large for a bit string length of '" + bitStringLength + "'");
+ }
+
while (bitString.length() < bitStringLength) {
bitString = "0" + bitString;
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedStringEncoder.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedStringEncoder.java
index 5cb8762a..38db681e 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedStringEncoder.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/FixedStringEncoder.java
@@ -8,7 +8,7 @@ public class FixedStringEncoder {
private static Pattern BITSTRING_VERIFICATION_PATTERN = Pattern.compile("^[0-1]*$", Pattern.CASE_INSENSITIVE);
- public static String encode(String value, int stringLength) throws EncodingException {
+ public static String encode(String value, int stringLength) {
while (value.length() < stringLength) {
value += " ";
}
@@ -29,7 +29,7 @@ public static String encode(String value, int stringLength) throws EncodingExcep
return bitString;
}
- public static String decode(String bitString) throws DecodingException {
+ public static String decode(String bitString) {
if (!BITSTRING_VERIFICATION_PATTERN.matcher(bitString).matches() || bitString.length() % 6 != 0) {
throw new DecodingException("Undecodable FixedString '" + bitString + "'");
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/OptimizedFibonacciRangeEncoder.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/OptimizedFibonacciRangeEncoder.java
new file mode 100644
index 00000000..3492fbcc
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/OptimizedFibonacciRangeEncoder.java
@@ -0,0 +1,56 @@
+package com.iab.gpp.encoder.datatype.encoder;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+import com.iab.gpp.encoder.error.DecodingException;
+import com.iab.gpp.encoder.error.EncodingException;
+
+public class OptimizedFibonacciRangeEncoder {
+
+ private static Pattern BITSTRING_VERIFICATION_PATTERN = Pattern.compile("^[0-1]*$", Pattern.CASE_INSENSITIVE);
+
+ public static String encode(List value) throws EncodingException {
+ // TODO: encoding the range before choosing the shortest is inefficient. There is probably a way
+ // to identify in advance which will be shorter based on the array length and values
+ int max = value.size() > 0 ? value.get(value.size() - 1) : 0;
+ String rangeBitString = FibonacciIntegerRangeEncoder.encode(value);
+ int rangeLength = rangeBitString.length();
+ int bitFieldLength = max;
+
+ if (rangeLength <= bitFieldLength) {
+ return FixedIntegerEncoder.encode(max, 16) + "1" + rangeBitString;
+ } else {
+ List bits = new ArrayList<>();
+ int index = 0;
+ for (int i = 0; i < max; i++) {
+ if (i == value.get(index) - 1) {
+ bits.add(true);
+ index++;
+ } else {
+ bits.add(false);
+ }
+ }
+ return FixedIntegerEncoder.encode(max, 16) + "0" + FixedBitfieldEncoder.encode(bits, bitFieldLength);
+ }
+ }
+
+ public static List decode(String bitString) throws DecodingException {
+ if (!BITSTRING_VERIFICATION_PATTERN.matcher(bitString).matches() || bitString.length() < 12) {
+ throw new DecodingException("Undecodable FibonacciIntegerRange '" + bitString + "'");
+ }
+
+ if (bitString.charAt(16) == '1') {
+ return FibonacciIntegerRangeEncoder.decode(bitString.substring(17));
+ } else {
+ List value = new ArrayList<>();
+ List bits = FixedBitfieldEncoder.decode(bitString.substring(17));
+ for (int i = 0; i < bits.size(); i++) {
+ if (bits.get(i) == true) {
+ value.add(i + 1);
+ }
+ }
+ return value;
+ }
+ }
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/OptimizedFixedRangeEncoder.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/OptimizedFixedRangeEncoder.java
new file mode 100644
index 00000000..73fb5e68
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/encoder/OptimizedFixedRangeEncoder.java
@@ -0,0 +1,57 @@
+package com.iab.gpp.encoder.datatype.encoder;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+import com.iab.gpp.encoder.error.DecodingException;
+import com.iab.gpp.encoder.error.EncodingException;
+
+public class OptimizedFixedRangeEncoder {
+
+ private static Pattern BITSTRING_VERIFICATION_PATTERN = Pattern.compile("^[0-1]*$", Pattern.CASE_INSENSITIVE);
+
+ public static String encode(List value) throws EncodingException {
+ // TODO: encoding the range before choosing the shortest is inefficient. There is probably a way
+ // to identify in advance which will be shorter based on the array length and values
+ int max = value.size() > 0 ? value.get(value.size() - 1) : 0;
+ String rangeBitString = FixedIntegerRangeEncoder.encode(value);
+ int rangeLength = rangeBitString.length();
+ int bitFieldLength = max;
+
+ if (rangeLength <= bitFieldLength) {
+ return FixedIntegerEncoder.encode(max, 16) + "1" + rangeBitString;
+ } else {
+ List bits = new ArrayList<>();
+ int index = 0;
+ for (int i = 0; i < max; i++) {
+ if (i == value.get(index) - 1) {
+ bits.add(true);
+ index++;
+ } else {
+ bits.add(false);
+ }
+ }
+
+ return FixedIntegerEncoder.encode(max, 16) + "0" + FixedBitfieldEncoder.encode(bits, bitFieldLength);
+ }
+ }
+
+ public static List decode(String bitString) throws DecodingException {
+ if (!BITSTRING_VERIFICATION_PATTERN.matcher(bitString).matches() || bitString.length() < 12) {
+ throw new DecodingException("Undecodable FixedIntegerRange '" + bitString + "'");
+ }
+
+ if (bitString.charAt(16) == '1') {
+ return FixedIntegerRangeEncoder.decode(bitString.substring(17));
+ } else {
+ List value = new ArrayList<>();
+ List bits = FixedBitfieldEncoder.decode(bitString.substring(17));
+ for (int i = 0; i < bits.size(); i++) {
+ if (bits.get(i) == true) {
+ value.add(i + 1);
+ }
+ }
+ return value;
+ }
+ }
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/error/DecodingException.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/error/DecodingException.java
index 9ea130c1..d60c3459 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/error/DecodingException.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/error/DecodingException.java
@@ -1,10 +1,18 @@
package com.iab.gpp.encoder.error;
-public class DecodingException extends Exception {
+public class DecodingException extends RuntimeException {
private static final long serialVersionUID = 2098268445119981680L;
public DecodingException(String msg) {
super(msg);
}
+
+ public DecodingException(Exception e) {
+ super(e);
+ }
+
+ public DecodingException(String msg, Exception e) {
+ super(msg, e);
+ }
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/error/EncodingException.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/error/EncodingException.java
index 57ec5efb..a7f19355 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/error/EncodingException.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/error/EncodingException.java
@@ -1,10 +1,18 @@
package com.iab.gpp.encoder.error;
-public class EncodingException extends Exception {
+public class EncodingException extends RuntimeException {
private static final long serialVersionUID = 1161321945571871601L;
public EncodingException(String msg) {
super(msg);
}
+
+ public EncodingException(Exception e) {
+ super(e);
+ }
+
+ public EncodingException(String msg, Exception e) {
+ super(msg, e);
+ }
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/error/InvalidFieldException.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/error/InvalidFieldException.java
new file mode 100644
index 00000000..22113280
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/error/InvalidFieldException.java
@@ -0,0 +1,18 @@
+package com.iab.gpp.encoder.error;
+
+public class InvalidFieldException extends RuntimeException {
+
+ private static final long serialVersionUID = 2098268445119981680L;
+
+ public InvalidFieldException(String msg) {
+ super(msg);
+ }
+
+ public InvalidFieldException(Exception e) {
+ super(e);
+ }
+
+ public InvalidFieldException(String msg, Exception e) {
+ super(msg, e);
+ }
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/error/ValidationException.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/error/ValidationException.java
new file mode 100644
index 00000000..733a740b
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/error/ValidationException.java
@@ -0,0 +1,18 @@
+package com.iab.gpp.encoder.error;
+
+public class ValidationException extends DecodingException {
+
+ private static final long serialVersionUID = 2098268445119981680L;
+
+ public ValidationException(String msg) {
+ super(msg);
+ }
+
+ public ValidationException(Exception e) {
+ super(e);
+ }
+
+ public ValidationException(String msg, Exception e) {
+ super(msg, e);
+ }
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/EncodableBitStringFields.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/EncodableBitStringFields.java
new file mode 100644
index 00000000..d2ac0f43
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/EncodableBitStringFields.java
@@ -0,0 +1,31 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.HashMap;
+import java.util.Map;
+import com.iab.gpp.encoder.datatype.AbstractEncodableBitStringDataType;
+
+public class EncodableBitStringFields implements Fields> {
+
+ private Map> fields = new HashMap<>();
+
+ public boolean containsKey(String key) {
+ return this.fields.containsKey(key);
+ }
+
+ public void put(String key, AbstractEncodableBitStringDataType> value) {
+ this.fields.put(key, value);
+ }
+
+ public AbstractEncodableBitStringDataType> get(String key) {
+ return this.fields.get(key);
+ }
+
+ public Map> getAll() {
+ return new HashMap<>(this.fields);
+ }
+
+ public void reset(Fields> fields) {
+ this.fields.clear();
+ this.fields.putAll(fields.getAll());
+ }
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/Fields.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/Fields.java
new file mode 100644
index 00000000..ca091e7f
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/Fields.java
@@ -0,0 +1,14 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.Map;
+import com.iab.gpp.encoder.datatype.DataType;
+
+public interface Fields> {
+
+ boolean containsKey(String key);
+ void put(String key, T value);
+ T get(String key);
+ Map getAll();
+ void reset(Fields fields);
+
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/GenericFields.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/GenericFields.java
new file mode 100644
index 00000000..00263e55
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/GenericFields.java
@@ -0,0 +1,31 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.HashMap;
+import java.util.Map;
+import com.iab.gpp.encoder.datatype.DataType;
+
+public class GenericFields implements Fields> {
+
+ private Map> fields = new HashMap<>();
+
+ public boolean containsKey(String key) {
+ return this.fields.containsKey(key);
+ }
+
+ public void put(String key, DataType> value) {
+ this.fields.put(key, value);
+ }
+
+ public DataType> get(String key) {
+ return this.fields.get(key);
+ }
+
+ public Map> getAll() {
+ return new HashMap<>(this.fields);
+ }
+
+ public void reset(Fields> fields) {
+ this.fields.clear();
+ this.fields.putAll(fields.getAll());
+ }
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/HeaderV1Field.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/HeaderV1Field.java
index 384befab..17f1f5d9 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/HeaderV1Field.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/HeaderV1Field.java
@@ -1,9 +1,21 @@
package com.iab.gpp.encoder.field;
+import java.util.Arrays;
+import java.util.List;
+
public class HeaderV1Field {
public static String ID = "Id";
public static String VERSION = "Version";
public static String SECTION_IDS = "SectionIds";
+ //@formatter:off
+ public static List HEADER_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ HeaderV1Field.ID,
+ HeaderV1Field.VERSION,
+ HeaderV1Field.SECTION_IDS
+ });
+ //@formatter:on
+
+
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/TcfCaV1Field.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/TcfCaV1Field.java
new file mode 100644
index 00000000..c87e886d
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/TcfCaV1Field.java
@@ -0,0 +1,73 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class TcfCaV1Field {
+
+ public static String VERSION = "Version";
+ public static String CREATED = "Created";
+ public static String LAST_UPDATED = "LastUpdated";
+ public static String CMP_ID = "CmpId";
+ public static String CMP_VERSION = "CmpVersion";
+ public static String CONSENT_SCREEN = "ConsentScreen";
+ public static String CONSENT_LANGUAGE = "ConsentLanguage";
+ public static String VENDOR_LIST_VERSION = "VendorListVersion";
+ public static String TCF_POLICY_VERSION = "TcfPolicyVersion";
+ public static String USE_NON_STANDARD_STACKS = "UseNonStandardStacks";
+ public static String SPECIAL_FEATURE_EXPRESS_CONSENT = "SpecialFeatureExpressConsent";
+ public static String PURPOSES_EXPRESS_CONSENT = "PurposesExpressConsent";
+ public static String PURPOSES_IMPLIED_CONSENT = "PurposesImpliedConsent";
+ public static String VENDOR_EXPRESS_CONSENT = "VendorExpressConsent";
+ public static String VENDOR_IMPLIED_CONSENT = "VendorImpliedConsent";
+ public static String PUB_RESTRICTIONS = "PubRestrictions";
+
+ public static String PUB_PURPOSES_SEGMENT_TYPE = "PubPurposesSegmentType";
+ public static String PUB_PURPOSES_EXPRESS_CONSENT = "PubPurposesExpressConsent";
+ public static String PUB_PURPOSES_IMPLIED_CONSENT = "PubPurposesImpliedConsent";
+ public static String NUM_CUSTOM_PURPOSES = "NumCustomPurposes";
+ public static String CUSTOM_PURPOSES_EXPRESS_CONSENT = "CustomPurposesExpressConsent";
+ public static String CUSTOM_PURPOSES_IMPLIED_CONSENT = "CustomPurposesImpliedConsent";
+
+ public static String DISCLOSED_VENDORS_SEGMENT_TYPE = "DisclosedVendorsSegmentType";
+ public static String DISCLOSED_VENDORS = "DisclosedVendors";
+
+ //@formatter:off
+ public static List TCFCAV1_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ TcfCaV1Field.VERSION,
+ TcfCaV1Field.CREATED,
+ TcfCaV1Field.LAST_UPDATED,
+ TcfCaV1Field.CMP_ID,
+ TcfCaV1Field.CMP_VERSION,
+ TcfCaV1Field.CONSENT_SCREEN,
+ TcfCaV1Field.CONSENT_LANGUAGE,
+ TcfCaV1Field.VENDOR_LIST_VERSION,
+ TcfCaV1Field.TCF_POLICY_VERSION,
+ TcfCaV1Field.USE_NON_STANDARD_STACKS,
+ TcfCaV1Field.SPECIAL_FEATURE_EXPRESS_CONSENT,
+ TcfCaV1Field.PURPOSES_EXPRESS_CONSENT,
+ TcfCaV1Field.PURPOSES_IMPLIED_CONSENT,
+ TcfCaV1Field.VENDOR_EXPRESS_CONSENT,
+ TcfCaV1Field.VENDOR_IMPLIED_CONSENT,
+ TcfCaV1Field.PUB_RESTRICTIONS
+ });
+ //@formatter:on
+
+ //@formatter:off
+ public static List TCFCAV1_PUBLISHER_PURPOSES_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ TcfCaV1Field.PUB_PURPOSES_SEGMENT_TYPE,
+ TcfCaV1Field.PUB_PURPOSES_EXPRESS_CONSENT,
+ TcfCaV1Field.PUB_PURPOSES_IMPLIED_CONSENT,
+ TcfCaV1Field.NUM_CUSTOM_PURPOSES,
+ TcfCaV1Field.CUSTOM_PURPOSES_EXPRESS_CONSENT,
+ TcfCaV1Field.CUSTOM_PURPOSES_IMPLIED_CONSENT,
+ });
+ //@formatter:on
+
+ //@formatter:off
+ public static List TCFCAV1_DISCLOSED_VENDORS_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ TcfCaV1Field.DISCLOSED_VENDORS_SEGMENT_TYPE,
+ TcfCaV1Field.DISCLOSED_VENDORS,
+ });
+ //@formatter:on
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/TcfCaV2Field.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/TcfCaV2Field.java
deleted file mode 100644
index 2aeba45b..00000000
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/TcfCaV2Field.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.iab.gpp.encoder.field;
-
-public class TcfCaV2Field {
-
- public static String VERSION = "Version";
- public static String CREATED = "Created";
- public static String LAST_UPDATED = "LastUpdated";
- public static String CMP_ID = "CmpId";
- public static String CMP_VERSION = "CmpVersion";
- public static String CONSENT_SCREEN = "ConsentScreen";
- public static String CONSENT_LANGUAGE = "ConsentLanguage";
- public static String VENDOR_LIST_VERSION = "VendorListVersion";
- public static String TCF_POLICY_VERSION = "TcfPolicyVersion";
- public static String USE_NON_STANDARD_STACKS = "UseNonStandardStacks";
- public static String SPECIAL_FEATURE_EXPRESS_CONSENT = "SpecialFeatureExpressConsent";
- public static String PURPOSES_EXPRESS_CONSENT = "PurposesExpressConsent";
- public static String PURPOSES_IMPLIED_CONSENT = "PurposesImpliedConsent";
- public static String VENDOR_EXPRESS_CONSENT = "VendorExpressConsent";
- public static String VENDOR_IMPLIED_CONSENT = "VendorImpliedConsent";
- public static String SEGMENT_TYPE = "SegmentType";
- public static String PUB_PURPOSES_EXPRESS_CONSENT = "PubPurposesExpressConsent";
- public static String PUB_PURPOSES_IMPLIED_CONSENT = "PubPurposesImpliedConsent";
- public static String NUM_CUSTOM_PURPOSES = "NumCustomPurposes";
- public static String CUSTOM_PURPOSES_EXPRESS_CONSENT = "CustomPurposesExpressConsent";
- public static String CUSTOM_PURPOSES_IMPLIED_CONSENT = "CustomPurposesImpliedConsent";
-
-}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/TcfEuV2Field.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/TcfEuV2Field.java
index 660443c3..fedb510f 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/TcfEuV2Field.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/TcfEuV2Field.java
@@ -1,5 +1,8 @@
package com.iab.gpp.encoder.field;
+import java.util.Arrays;
+import java.util.List;
+
public class TcfEuV2Field {
public static String VERSION = "Version";
@@ -32,4 +35,52 @@ public class TcfEuV2Field {
public static String VENDORS_DISCLOSED_SEGMENT_TYPE = "VendorsDisclosedSegmentType";
public static String VENDORS_DISCLOSED = "VendorsDisclosed";
+ //@formatter:off
+ public static List TCFEUV2_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ TcfEuV2Field.VERSION,
+ TcfEuV2Field.CREATED,
+ TcfEuV2Field.LAST_UPDATED,
+ TcfEuV2Field.CMP_ID,
+ TcfEuV2Field.CMP_VERSION,
+ TcfEuV2Field.CONSENT_SCREEN,
+ TcfEuV2Field.CONSENT_LANGUAGE,
+ TcfEuV2Field.VENDOR_LIST_VERSION,
+ TcfEuV2Field.POLICY_VERSION,
+ TcfEuV2Field.IS_SERVICE_SPECIFIC,
+ TcfEuV2Field.USE_NON_STANDARD_STACKS,
+ TcfEuV2Field.SPECIAL_FEATURE_OPTINS,
+ TcfEuV2Field.PURPOSE_CONSENTS,
+ TcfEuV2Field.PURPOSE_LEGITIMATE_INTERESTS,
+ TcfEuV2Field.PURPOSE_ONE_TREATMENT,
+ TcfEuV2Field.PUBLISHER_COUNTRY_CODE,
+ TcfEuV2Field.VENDOR_CONSENTS,
+ TcfEuV2Field.VENDOR_LEGITIMATE_INTERESTS,
+ TcfEuV2Field.PUBLISHER_RESTRICTIONS
+ });
+ //@formatter:on
+
+ //@formatter:off
+ public static List TCFEUV2_PUBLISHER_PURPOSES_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ TcfEuV2Field.PUBLISHER_PURPOSES_SEGMENT_TYPE,
+ TcfEuV2Field.PUBLISHER_CONSENTS,
+ TcfEuV2Field.PUBLISHER_LEGITIMATE_INTERESTS,
+ TcfEuV2Field.NUM_CUSTOM_PURPOSES,
+ TcfEuV2Field.PUBLISHER_CUSTOM_CONSENTS,
+ TcfEuV2Field.PUBLISHER_CUSTOM_LEGITIMATE_INTERESTS,
+ });
+ //@formatter:on
+
+ //@formatter:off
+ public static List TCFEUV2_VENDORS_ALLOWED_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ TcfEuV2Field.VENDORS_ALLOWED_SEGMENT_TYPE,
+ TcfEuV2Field.VENDORS_ALLOWED,
+ });
+ //@formatter:on
+
+ //@formatter:off
+ public static List TCFEUV2_VENDORS_DISCLOSED_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ TcfEuV2Field.VENDORS_DISCLOSED_SEGMENT_TYPE,
+ TcfEuV2Field.VENDORS_DISCLOSED,
+ });
+ //@formatter:on
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsCaField.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsCaField.java
new file mode 100644
index 00000000..934f57ac
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsCaField.java
@@ -0,0 +1,48 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class UsCaField {
+
+ public static String VERSION = "Version";
+ public static String SALE_OPT_OUT_NOTICE = "SaleOptOutNotice";
+ public static String SHARING_OPT_OUT_NOTICE = "SharingOptOutNotice";
+ public static String SENSITIVE_DATA_LIMIT_USE_NOTICE = "SensitiveDataLimitUseNotice";
+ public static String SALE_OPT_OUT = "SaleOptOut";
+ public static String SHARING_OPT_OUT = "SharingOptOut";
+ public static String SENSITIVE_DATA_PROCESSING = "SensitiveDataProcessing";
+ public static String KNOWN_CHILD_SENSITIVE_DATA_CONSENTS = "KnownChildSensitiveDataConsents";
+ public static String PERSONAL_DATA_CONSENTS = "PersonalDataConsents";
+ public static String MSPA_COVERED_TRANSACTION = "MspaCoveredTransaction";
+ public static String MSPA_OPT_OUT_OPTION_MODE = "MspaOptOutOptionMode";
+ public static String MSPA_SERVICE_PROVIDER_MODE = "MspaServiceProviderMode";
+
+ public static String GPC_SEGMENT_TYPE = "GpcSegmentType";
+ public static String GPC_SEGMENT_INCLUDED = "GpcSegmentIncluded";
+ public static String GPC = "Gpc";
+
+ //@formatter:off
+ public static List USCA_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsCaField.VERSION,
+ UsCaField.SALE_OPT_OUT_NOTICE,
+ UsCaField.SHARING_OPT_OUT_NOTICE,
+ UsCaField.SENSITIVE_DATA_LIMIT_USE_NOTICE,
+ UsCaField.SALE_OPT_OUT,
+ UsCaField.SHARING_OPT_OUT,
+ UsCaField.SENSITIVE_DATA_PROCESSING,
+ UsCaField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS,
+ UsCaField.PERSONAL_DATA_CONSENTS,
+ UsCaField.MSPA_COVERED_TRANSACTION,
+ UsCaField.MSPA_OPT_OUT_OPTION_MODE,
+ UsCaField.MSPA_SERVICE_PROVIDER_MODE
+ });
+ //@formatter:on
+
+ //@formatter:off
+ public static List USCA_GPC_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsCaField.GPC_SEGMENT_TYPE,
+ UsCaField.GPC
+ });
+ //@formatter:on
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsCoField.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsCoField.java
new file mode 100644
index 00000000..cae4e769
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsCoField.java
@@ -0,0 +1,46 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class UsCoField {
+
+ public static String VERSION = "Version";
+ public static String SHARING_NOTICE = "SharingNotice";
+ public static String SALE_OPT_OUT_NOTICE = "SaleOptOutNotice";
+ public static String TARGETED_ADVERTISING_OPT_OUT_NOTICE = "TargetedAdvertisingOptOutNotice";
+ public static String SALE_OPT_OUT = "SaleOptOut";
+ public static String TARGETED_ADVERTISING_OPT_OUT = "TargetedAdvertisingOptOut";
+ public static String SENSITIVE_DATA_PROCESSING = "SensitiveDataProcessing";
+ public static String KNOWN_CHILD_SENSITIVE_DATA_CONSENTS = "KnownChildSensitiveDataConsents";
+ public static String MSPA_COVERED_TRANSACTION = "MspaCoveredTransaction";
+ public static String MSPA_OPT_OUT_OPTION_MODE = "MspaOptOutOptionMode";
+ public static String MSPA_SERVICE_PROVIDER_MODE = "MspaServiceProviderMode";
+
+ public static String GPC_SEGMENT_TYPE = "GpcSegmentType";
+ public static String GPC_SEGMENT_INCLUDED = "GpcSegmentIncluded";
+ public static String GPC = "Gpc";
+
+ //@formatter:off
+ public static List USCO_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsCoField.VERSION,
+ UsCoField.SHARING_NOTICE,
+ UsCoField.SALE_OPT_OUT_NOTICE,
+ UsCoField.TARGETED_ADVERTISING_OPT_OUT_NOTICE,
+ UsCoField.SALE_OPT_OUT,
+ UsCoField.TARGETED_ADVERTISING_OPT_OUT,
+ UsCoField.SENSITIVE_DATA_PROCESSING,
+ UsCoField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS,
+ UsCoField.MSPA_COVERED_TRANSACTION,
+ UsCoField.MSPA_OPT_OUT_OPTION_MODE,
+ UsCoField.MSPA_SERVICE_PROVIDER_MODE
+ });
+ //@formatter:on
+
+ //@formatter:off
+ public static List USCO_GPC_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsCoField.GPC_SEGMENT_TYPE,
+ UsCoField.GPC
+ });
+ //@formatter:on
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsCtField.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsCtField.java
new file mode 100644
index 00000000..9662e1e3
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsCtField.java
@@ -0,0 +1,46 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class UsCtField {
+
+ public static String VERSION = "Version";
+ public static String SHARING_NOTICE = "SharingNotice";
+ public static String SALE_OPT_OUT_NOTICE = "SaleOptOutNotice";
+ public static String TARGETED_ADVERTISING_OPT_OUT_NOTICE = "TargetedAdvertisingOptOutNotice";
+ public static String SALE_OPT_OUT = "SaleOptOut";
+ public static String TARGETED_ADVERTISING_OPT_OUT = "TargetedAdvertisingOptOut";
+ public static String SENSITIVE_DATA_PROCESSING = "SensitiveDataProcessing";
+ public static String KNOWN_CHILD_SENSITIVE_DATA_CONSENTS = "KnownChildSensitiveDataConsents";
+ public static String MSPA_COVERED_TRANSACTION = "MspaCoveredTransaction";
+ public static String MSPA_OPT_OUT_OPTION_MODE = "MspaOptOutOptionMode";
+ public static String MSPA_SERVICE_PROVIDER_MODE = "MspaServiceProviderMode";
+
+ public static String GPC_SEGMENT_TYPE = "GpcSegmentType";
+ public static String GPC_SEGMENT_INCLUDED = "GpcSegmentIncluded";
+ public static String GPC = "Gpc";
+
+ //@formatter:off
+ public static List USCT_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsCtField.VERSION,
+ UsCtField.SHARING_NOTICE,
+ UsCtField.SALE_OPT_OUT_NOTICE,
+ UsCtField.TARGETED_ADVERTISING_OPT_OUT_NOTICE,
+ UsCtField.SALE_OPT_OUT,
+ UsCtField.TARGETED_ADVERTISING_OPT_OUT,
+ UsCtField.SENSITIVE_DATA_PROCESSING,
+ UsCtField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS,
+ UsCtField.MSPA_COVERED_TRANSACTION,
+ UsCtField.MSPA_OPT_OUT_OPTION_MODE,
+ UsCtField.MSPA_SERVICE_PROVIDER_MODE
+ });
+ //@formatter:on
+
+ //@formatter:off
+ public static List USCT_GPC_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsCtField.GPC_SEGMENT_TYPE,
+ UsCtField.GPC
+ });
+ //@formatter:on
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsDeField.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsDeField.java
new file mode 100644
index 00000000..2979f9b6
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsDeField.java
@@ -0,0 +1,48 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class UsDeField {
+
+ public static String VERSION = "Version";
+ public static String PROCESSING_NOTICE = "ProcessingNotice";
+ public static String SALE_OPT_OUT_NOTICE = "SaleOptOutNotice";
+ public static String TARGETED_ADVERTISING_OPT_OUT_NOTICE = "TargetedAdvertisingOptOutNotice";
+ public static String SALE_OPT_OUT = "SaleOptOut";
+ public static String TARGETED_ADVERTISING_OPT_OUT = "TargetedAdvertisingOptOut";
+ public static String SENSITIVE_DATA_PROCESSING = "SensitiveDataProcessing";
+ public static String KNOWN_CHILD_SENSITIVE_DATA_CONSENTS = "KnownChildSensitiveDataConsents";
+ public static String ADDITIONAL_DATA_PROCESSING_CONSENT = "AdditionalDataProcessingConsent";
+ public static String MSPA_COVERED_TRANSACTION = "MspaCoveredTransaction";
+ public static String MSPA_OPT_OUT_OPTION_MODE = "MspaOptOutOptionMode";
+ public static String MSPA_SERVICE_PROVIDER_MODE = "MspaServiceProviderMode";
+
+ public static String GPC_SEGMENT_TYPE = "GpcSegmentType";
+ public static String GPC_SEGMENT_INCLUDED = "GpcSegmentIncluded";
+ public static String GPC = "Gpc";
+
+ //@formatter:off
+ public static List USDE_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsDeField.VERSION,
+ UsDeField.PROCESSING_NOTICE,
+ UsDeField.SALE_OPT_OUT_NOTICE,
+ UsDeField.TARGETED_ADVERTISING_OPT_OUT_NOTICE,
+ UsDeField.SALE_OPT_OUT,
+ UsDeField.TARGETED_ADVERTISING_OPT_OUT,
+ UsDeField.SENSITIVE_DATA_PROCESSING,
+ UsDeField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS,
+ UsDeField.ADDITIONAL_DATA_PROCESSING_CONSENT,
+ UsDeField.MSPA_COVERED_TRANSACTION,
+ UsDeField.MSPA_OPT_OUT_OPTION_MODE,
+ UsDeField.MSPA_SERVICE_PROVIDER_MODE
+ });
+ //@formatter:on
+
+ //@formatter:off
+ public static List USDE_GPC_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsDeField.GPC_SEGMENT_TYPE,
+ UsDeField.GPC
+ });
+ //@formatter:on
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsFlField.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsFlField.java
new file mode 100644
index 00000000..f3647a79
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsFlField.java
@@ -0,0 +1,37 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class UsFlField {
+
+ public static String VERSION = "Version";
+ public static String PROCESSING_NOTICE = "ProcessingNotice";
+ public static String SALE_OPT_OUT_NOTICE = "SaleOptOutNotice";
+ public static String TARGETED_ADVERTISING_OPT_OUT_NOTICE = "TargetedAdvertisingOptOutNotice";
+ public static String SALE_OPT_OUT = "SaleOptOut";
+ public static String TARGETED_ADVERTISING_OPT_OUT = "TargetedAdvertisingOptOut";
+ public static String SENSITIVE_DATA_PROCESSING = "SensitiveDataProcessing";
+ public static String KNOWN_CHILD_SENSITIVE_DATA_CONSENTS = "KnownChildSensitiveDataConsents";
+ public static String ADDITIONAL_DATA_PROCESSING_CONSENT = "AdditionalDataProcessingConsent";
+ public static String MSPA_COVERED_TRANSACTION = "MspaCoveredTransaction";
+ public static String MSPA_OPT_OUT_OPTION_MODE = "MspaOptOutOptionMode";
+ public static String MSPA_SERVICE_PROVIDER_MODE = "MspaServiceProviderMode";
+
+ //@formatter:off
+ public static List USFL_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsFlField.VERSION,
+ UsFlField.PROCESSING_NOTICE,
+ UsFlField.SALE_OPT_OUT_NOTICE,
+ UsFlField.TARGETED_ADVERTISING_OPT_OUT_NOTICE,
+ UsFlField.SALE_OPT_OUT,
+ UsFlField.TARGETED_ADVERTISING_OPT_OUT,
+ UsFlField.SENSITIVE_DATA_PROCESSING,
+ UsFlField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS,
+ UsFlField.ADDITIONAL_DATA_PROCESSING_CONSENT,
+ UsFlField.MSPA_COVERED_TRANSACTION,
+ UsFlField.MSPA_OPT_OUT_OPTION_MODE,
+ UsFlField.MSPA_SERVICE_PROVIDER_MODE
+ });
+ //@formatter:on
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsIaField.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsIaField.java
new file mode 100644
index 00000000..f0c8d523
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsIaField.java
@@ -0,0 +1,48 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class UsIaField {
+
+ public static String VERSION = "Version";
+ public static String PROCESSING_NOTICE = "ProcessingNotice";
+ public static String SALE_OPT_OUT_NOTICE = "SaleOptOutNotice";
+ public static String TARGETED_ADVERTISING_OPT_OUT_NOTICE = "TargetedAdvertisingOptOutNotice";
+ public static String SENSITIVE_DATA_OPT_OUT_NOTICE = "SensitiveDataOptOutNotice";
+ public static String SALE_OPT_OUT = "SaleOptOut";
+ public static String TARGETED_ADVERTISING_OPT_OUT = "TargetedAdvertisingOptOut";
+ public static String SENSITIVE_DATA_PROCESSING = "SensitiveDataProcessing";
+ public static String KNOWN_CHILD_SENSITIVE_DATA_CONSENTS = "KnownChildSensitiveDataConsents";
+ public static String MSPA_COVERED_TRANSACTION = "MspaCoveredTransaction";
+ public static String MSPA_OPT_OUT_OPTION_MODE = "MspaOptOutOptionMode";
+ public static String MSPA_SERVICE_PROVIDER_MODE = "MspaServiceProviderMode";
+
+ public static String GPC_SEGMENT_TYPE = "GpcSegmentType";
+ public static String GPC_SEGMENT_INCLUDED = "GpcSegmentIncluded";
+ public static String GPC = "Gpc";
+
+ //@formatter:off
+ public static List USIA_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsIaField.VERSION,
+ UsIaField.PROCESSING_NOTICE,
+ UsIaField.SALE_OPT_OUT_NOTICE,
+ UsIaField.TARGETED_ADVERTISING_OPT_OUT_NOTICE,
+ UsIaField.SENSITIVE_DATA_OPT_OUT_NOTICE,
+ UsIaField.SALE_OPT_OUT,
+ UsIaField.TARGETED_ADVERTISING_OPT_OUT,
+ UsIaField.SENSITIVE_DATA_PROCESSING,
+ UsIaField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS,
+ UsIaField.MSPA_COVERED_TRANSACTION,
+ UsIaField.MSPA_OPT_OUT_OPTION_MODE,
+ UsIaField.MSPA_SERVICE_PROVIDER_MODE
+ });
+ //@formatter:on
+
+ //@formatter:off
+ public static List USIA_GPC_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsIaField.GPC_SEGMENT_TYPE,
+ UsIaField.GPC
+ });
+ //@formatter:on
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsMtField.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsMtField.java
new file mode 100644
index 00000000..6b785f73
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsMtField.java
@@ -0,0 +1,48 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class UsMtField {
+
+ public static String VERSION = "Version";
+ public static String SHARING_NOTICE = "SharingNotice";
+ public static String SALE_OPT_OUT_NOTICE = "SaleOptOutNotice";
+ public static String TARGETED_ADVERTISING_OPT_OUT_NOTICE = "TargetedAdvertisingOptOutNotice";
+ public static String SALE_OPT_OUT = "SaleOptOut";
+ public static String TARGETED_ADVERTISING_OPT_OUT = "TargetedAdvertisingOptOut";
+ public static String SENSITIVE_DATA_PROCESSING = "SensitiveDataProcessing";
+ public static String KNOWN_CHILD_SENSITIVE_DATA_CONSENTS = "KnownChildSensitiveDataConsents";
+ public static String ADDITIONAL_DATA_PROCESSING_CONSENT = "AdditionalDataProcessingConsent";
+ public static String MSPA_COVERED_TRANSACTION = "MspaCoveredTransaction";
+ public static String MSPA_OPT_OUT_OPTION_MODE = "MspaOptOutOptionMode";
+ public static String MSPA_SERVICE_PROVIDER_MODE = "MspaServiceProviderMode";
+
+ public static String GPC_SEGMENT_TYPE = "GpcSegmentType";
+ public static String GPC_SEGMENT_INCLUDED = "GpcSegmentIncluded";
+ public static String GPC = "Gpc";
+
+ //@formatter:off
+ public static List USMT_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsMtField.VERSION,
+ UsMtField.SHARING_NOTICE,
+ UsMtField.SALE_OPT_OUT_NOTICE,
+ UsMtField.TARGETED_ADVERTISING_OPT_OUT_NOTICE,
+ UsMtField.SALE_OPT_OUT,
+ UsMtField.TARGETED_ADVERTISING_OPT_OUT,
+ UsMtField.SENSITIVE_DATA_PROCESSING,
+ UsMtField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS,
+ UsMtField.ADDITIONAL_DATA_PROCESSING_CONSENT,
+ UsMtField.MSPA_COVERED_TRANSACTION,
+ UsMtField.MSPA_OPT_OUT_OPTION_MODE,
+ UsMtField.MSPA_SERVICE_PROVIDER_MODE
+ });
+ //@formatter:on
+
+ //@formatter:off
+ public static List USMT_GPC_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsMtField.GPC_SEGMENT_TYPE,
+ UsMtField.GPC
+ });
+ //@formatter:on
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsNatField.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsNatField.java
new file mode 100644
index 00000000..e18427b1
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsNatField.java
@@ -0,0 +1,56 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class UsNatField {
+
+ public static String VERSION = "Version";
+ public static String SHARING_NOTICE = "SharingNotice";
+ public static String SALE_OPT_OUT_NOTICE = "SaleOptOutNotice";
+ public static String SHARING_OPT_OUT_NOTICE = "SharingOptOutNotice";
+ public static String TARGETED_ADVERTISING_OPT_OUT_NOTICE = "TargetedAdvertisingOptOutNotice";
+ public static String SENSITIVE_DATA_PROCESSING_OPT_OUT_NOTICE = "SensitiveDataProcessingOptOutNotice";
+ public static String SENSITIVE_DATA_LIMIT_USE_NOTICE = "SensitiveDataLimitUseNotice";
+ public static String SALE_OPT_OUT = "SaleOptOut";
+ public static String SHARING_OPT_OUT = "SharingOptOut";
+ public static String TARGETED_ADVERTISING_OPT_OUT = "TargetedAdvertisingOptOut";
+ public static String SENSITIVE_DATA_PROCESSING = "SensitiveDataProcessing";
+ public static String KNOWN_CHILD_SENSITIVE_DATA_CONSENTS = "KnownChildSensitiveDataConsents";
+ public static String PERSONAL_DATA_CONSENTS = "PersonalDataConsents";
+ public static String MSPA_COVERED_TRANSACTION = "MspaCoveredTransaction";
+ public static String MSPA_OPT_OUT_OPTION_MODE = "MspaOptOutOptionMode";
+ public static String MSPA_SERVICE_PROVIDER_MODE = "MspaServiceProviderMode";
+
+ public static String GPC_SEGMENT_TYPE = "GpcSegmentType";
+ public static String GPC_SEGMENT_INCLUDED = "GpcSegmentIncluded";
+ public static String GPC = "Gpc";
+
+ //@formatter:off
+ public static List USNAT_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsNatField.VERSION,
+ UsNatField.SHARING_NOTICE,
+ UsNatField.SALE_OPT_OUT_NOTICE,
+ UsNatField.SHARING_OPT_OUT_NOTICE,
+ UsNatField.TARGETED_ADVERTISING_OPT_OUT_NOTICE,
+ UsNatField.SENSITIVE_DATA_PROCESSING_OPT_OUT_NOTICE,
+ UsNatField.SENSITIVE_DATA_LIMIT_USE_NOTICE,
+ UsNatField.SALE_OPT_OUT,
+ UsNatField.SHARING_OPT_OUT,
+ UsNatField.TARGETED_ADVERTISING_OPT_OUT,
+ UsNatField.SENSITIVE_DATA_PROCESSING,
+ UsNatField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS,
+ UsNatField.PERSONAL_DATA_CONSENTS,
+ UsNatField.MSPA_COVERED_TRANSACTION,
+ UsNatField.MSPA_OPT_OUT_OPTION_MODE,
+ UsNatField.MSPA_SERVICE_PROVIDER_MODE
+ });
+ //@formatter:on
+
+ //@formatter:off
+ public static List USNAT_GPC_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsNatField.GPC_SEGMENT_TYPE,
+ UsNatField.GPC
+ });
+ //@formatter:on
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsNeField.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsNeField.java
new file mode 100644
index 00000000..a4479a87
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsNeField.java
@@ -0,0 +1,48 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class UsNeField {
+
+ public static String VERSION = "Version";
+ public static String PROCESSING_NOTICE = "ProcessingNotice";
+ public static String SALE_OPT_OUT_NOTICE = "SaleOptOutNotice";
+ public static String TARGETED_ADVERTISING_OPT_OUT_NOTICE = "TargetedAdvertisingOptOutNotice";
+ public static String SALE_OPT_OUT = "SaleOptOut";
+ public static String TARGETED_ADVERTISING_OPT_OUT = "TargetedAdvertisingOptOut";
+ public static String SENSITIVE_DATA_PROCESSING = "SensitiveDataProcessing";
+ public static String KNOWN_CHILD_SENSITIVE_DATA_CONSENTS = "KnownChildSensitiveDataConsents";
+ public static String ADDITIONAL_DATA_PROCESSING_CONSENT = "AdditionalDataProcessingConsent";
+ public static String MSPA_COVERED_TRANSACTION = "MspaCoveredTransaction";
+ public static String MSPA_OPT_OUT_OPTION_MODE = "MspaOptOutOptionMode";
+ public static String MSPA_SERVICE_PROVIDER_MODE = "MspaServiceProviderMode";
+
+ public static String GPC_SEGMENT_TYPE = "GpcSegmentType";
+ public static String GPC_SEGMENT_INCLUDED = "GpcSegmentIncluded";
+ public static String GPC = "Gpc";
+
+ //@formatter:off
+ public static List USNE_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsNeField.VERSION,
+ UsNeField.PROCESSING_NOTICE,
+ UsNeField.SALE_OPT_OUT_NOTICE,
+ UsNeField.TARGETED_ADVERTISING_OPT_OUT_NOTICE,
+ UsNeField.SALE_OPT_OUT,
+ UsNeField.TARGETED_ADVERTISING_OPT_OUT,
+ UsNeField.SENSITIVE_DATA_PROCESSING,
+ UsNeField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS,
+ UsNeField.ADDITIONAL_DATA_PROCESSING_CONSENT,
+ UsNeField.MSPA_COVERED_TRANSACTION,
+ UsNeField.MSPA_OPT_OUT_OPTION_MODE,
+ UsNeField.MSPA_SERVICE_PROVIDER_MODE
+ });
+ //@formatter:on
+
+ //@formatter:off
+ public static List USNE_GPC_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsNeField.GPC_SEGMENT_TYPE,
+ UsNeField.GPC
+ });
+ //@formatter:on
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsNhField.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsNhField.java
new file mode 100644
index 00000000..8381dc9b
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsNhField.java
@@ -0,0 +1,48 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class UsNhField {
+
+ public static String VERSION = "Version";
+ public static String PROCESSING_NOTICE = "ProcessingNotice";
+ public static String SALE_OPT_OUT_NOTICE = "SaleOptOutNotice";
+ public static String TARGETED_ADVERTISING_OPT_OUT_NOTICE = "TargetedAdvertisingOptOutNotice";
+ public static String SALE_OPT_OUT = "SaleOptOut";
+ public static String TARGETED_ADVERTISING_OPT_OUT = "TargetedAdvertisingOptOut";
+ public static String SENSITIVE_DATA_PROCESSING = "SensitiveDataProcessing";
+ public static String KNOWN_CHILD_SENSITIVE_DATA_CONSENTS = "KnownChildSensitiveDataConsents";
+ public static String ADDITIONAL_DATA_PROCESSING_CONSENT = "AdditionalDataProcessingConsent";
+ public static String MSPA_COVERED_TRANSACTION = "MspaCoveredTransaction";
+ public static String MSPA_OPT_OUT_OPTION_MODE = "MspaOptOutOptionMode";
+ public static String MSPA_SERVICE_PROVIDER_MODE = "MspaServiceProviderMode";
+
+ public static String GPC_SEGMENT_TYPE = "GpcSegmentType";
+ public static String GPC_SEGMENT_INCLUDED = "GpcSegmentIncluded";
+ public static String GPC = "Gpc";
+
+ //@formatter:off
+ public static List USNH_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsNhField.VERSION,
+ UsNhField.PROCESSING_NOTICE,
+ UsNhField.SALE_OPT_OUT_NOTICE,
+ UsNhField.TARGETED_ADVERTISING_OPT_OUT_NOTICE,
+ UsNhField.SALE_OPT_OUT,
+ UsNhField.TARGETED_ADVERTISING_OPT_OUT,
+ UsNhField.SENSITIVE_DATA_PROCESSING,
+ UsNhField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS,
+ UsNhField.ADDITIONAL_DATA_PROCESSING_CONSENT,
+ UsNhField.MSPA_COVERED_TRANSACTION,
+ UsNhField.MSPA_OPT_OUT_OPTION_MODE,
+ UsNhField.MSPA_SERVICE_PROVIDER_MODE
+ });
+ //@formatter:on
+
+ //@formatter:off
+ public static List USNH_GPC_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsNhField.GPC_SEGMENT_TYPE,
+ UsNhField.GPC
+ });
+ //@formatter:on
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsNjField.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsNjField.java
new file mode 100644
index 00000000..2acba850
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsNjField.java
@@ -0,0 +1,48 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class UsNjField {
+
+ public static String VERSION = "Version";
+ public static String PROCESSING_NOTICE = "ProcessingNotice";
+ public static String SALE_OPT_OUT_NOTICE = "SaleOptOutNotice";
+ public static String TARGETED_ADVERTISING_OPT_OUT_NOTICE = "TargetedAdvertisingOptOutNotice";
+ public static String SALE_OPT_OUT = "SaleOptOut";
+ public static String TARGETED_ADVERTISING_OPT_OUT = "TargetedAdvertisingOptOut";
+ public static String SENSITIVE_DATA_PROCESSING = "SensitiveDataProcessing";
+ public static String KNOWN_CHILD_SENSITIVE_DATA_CONSENTS = "KnownChildSensitiveDataConsents";
+ public static String ADDITIONAL_DATA_PROCESSING_CONSENT = "AdditionalDataProcessingConsent";
+ public static String MSPA_COVERED_TRANSACTION = "MspaCoveredTransaction";
+ public static String MSPA_OPT_OUT_OPTION_MODE = "MspaOptOutOptionMode";
+ public static String MSPA_SERVICE_PROVIDER_MODE = "MspaServiceProviderMode";
+
+ public static String GPC_SEGMENT_TYPE = "GpcSegmentType";
+ public static String GPC_SEGMENT_INCLUDED = "GpcSegmentIncluded";
+ public static String GPC = "Gpc";
+
+ //@formatter:off
+ public static List USNJ_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsNjField.VERSION,
+ UsNjField.PROCESSING_NOTICE,
+ UsNjField.SALE_OPT_OUT_NOTICE,
+ UsNjField.TARGETED_ADVERTISING_OPT_OUT_NOTICE,
+ UsNjField.SALE_OPT_OUT,
+ UsNjField.TARGETED_ADVERTISING_OPT_OUT,
+ UsNjField.SENSITIVE_DATA_PROCESSING,
+ UsNjField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS,
+ UsNjField.ADDITIONAL_DATA_PROCESSING_CONSENT,
+ UsNjField.MSPA_COVERED_TRANSACTION,
+ UsNjField.MSPA_OPT_OUT_OPTION_MODE,
+ UsNjField.MSPA_SERVICE_PROVIDER_MODE
+ });
+ //@formatter:on
+
+ //@formatter:off
+ public static List USNJ_GPC_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsNjField.GPC_SEGMENT_TYPE,
+ UsNjField.GPC
+ });
+ //@formatter:on
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsOrField.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsOrField.java
new file mode 100644
index 00000000..3e9021c2
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsOrField.java
@@ -0,0 +1,48 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class UsOrField {
+
+ public static String VERSION = "Version";
+ public static String PROCESSING_NOTICE = "ProcessingNotice";
+ public static String SALE_OPT_OUT_NOTICE = "SaleOptOutNotice";
+ public static String TARGETED_ADVERTISING_OPT_OUT_NOTICE = "TargetedAdvertisingOptOutNotice";
+ public static String SALE_OPT_OUT = "SaleOptOut";
+ public static String TARGETED_ADVERTISING_OPT_OUT = "TargetedAdvertisingOptOut";
+ public static String SENSITIVE_DATA_PROCESSING = "SensitiveDataProcessing";
+ public static String KNOWN_CHILD_SENSITIVE_DATA_CONSENTS = "KnownChildSensitiveDataConsents";
+ public static String ADDITIONAL_DATA_PROCESSING_CONSENT = "AdditionalDataProcessingConsent";
+ public static String MSPA_COVERED_TRANSACTION = "MspaCoveredTransaction";
+ public static String MSPA_OPT_OUT_OPTION_MODE = "MspaOptOutOptionMode";
+ public static String MSPA_SERVICE_PROVIDER_MODE = "MspaServiceProviderMode";
+
+ public static String GPC_SEGMENT_TYPE = "GpcSegmentType";
+ public static String GPC_SEGMENT_INCLUDED = "GpcSegmentIncluded";
+ public static String GPC = "Gpc";
+
+ //@formatter:off
+ public static List USOR_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsOrField.VERSION,
+ UsOrField.PROCESSING_NOTICE,
+ UsOrField.SALE_OPT_OUT_NOTICE,
+ UsOrField.TARGETED_ADVERTISING_OPT_OUT_NOTICE,
+ UsOrField.SALE_OPT_OUT,
+ UsOrField.TARGETED_ADVERTISING_OPT_OUT,
+ UsOrField.SENSITIVE_DATA_PROCESSING,
+ UsOrField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS,
+ UsOrField.ADDITIONAL_DATA_PROCESSING_CONSENT,
+ UsOrField.MSPA_COVERED_TRANSACTION,
+ UsOrField.MSPA_OPT_OUT_OPTION_MODE,
+ UsOrField.MSPA_SERVICE_PROVIDER_MODE
+ });
+ //@formatter:on
+
+ //@formatter:off
+ public static List USOR_GPC_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsOrField.GPC_SEGMENT_TYPE,
+ UsOrField.GPC
+ });
+ //@formatter:on
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsTnField.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsTnField.java
new file mode 100644
index 00000000..7ef8c75d
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsTnField.java
@@ -0,0 +1,48 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class UsTnField {
+
+ public static String VERSION = "Version";
+ public static String PROCESSING_NOTICE = "ProcessingNotice";
+ public static String SALE_OPT_OUT_NOTICE = "SaleOptOutNotice";
+ public static String TARGETED_ADVERTISING_OPT_OUT_NOTICE = "TargetedAdvertisingOptOutNotice";
+ public static String SALE_OPT_OUT = "SaleOptOut";
+ public static String TARGETED_ADVERTISING_OPT_OUT = "TargetedAdvertisingOptOut";
+ public static String SENSITIVE_DATA_PROCESSING = "SensitiveDataProcessing";
+ public static String KNOWN_CHILD_SENSITIVE_DATA_CONSENTS = "KnownChildSensitiveDataConsents";
+ public static String ADDITIONAL_DATA_PROCESSING_CONSENT = "AdditionalDataProcessingConsent";
+ public static String MSPA_COVERED_TRANSACTION = "MspaCoveredTransaction";
+ public static String MSPA_OPT_OUT_OPTION_MODE = "MspaOptOutOptionMode";
+ public static String MSPA_SERVICE_PROVIDER_MODE = "MspaServiceProviderMode";
+
+ public static String GPC_SEGMENT_TYPE = "GpcSegmentType";
+ public static String GPC_SEGMENT_INCLUDED = "GpcSegmentIncluded";
+ public static String GPC = "Gpc";
+
+ //@formatter:off
+ public static List USTN_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsTnField.VERSION,
+ UsTnField.PROCESSING_NOTICE,
+ UsTnField.SALE_OPT_OUT_NOTICE,
+ UsTnField.TARGETED_ADVERTISING_OPT_OUT_NOTICE,
+ UsTnField.SALE_OPT_OUT,
+ UsTnField.TARGETED_ADVERTISING_OPT_OUT,
+ UsTnField.SENSITIVE_DATA_PROCESSING,
+ UsTnField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS,
+ UsTnField.ADDITIONAL_DATA_PROCESSING_CONSENT,
+ UsTnField.MSPA_COVERED_TRANSACTION,
+ UsTnField.MSPA_OPT_OUT_OPTION_MODE,
+ UsTnField.MSPA_SERVICE_PROVIDER_MODE
+ });
+ //@formatter:on
+
+ //@formatter:off
+ public static List USTN_GPC_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsTnField.GPC_SEGMENT_TYPE,
+ UsTnField.GPC
+ });
+ //@formatter:on
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsTxField.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsTxField.java
new file mode 100644
index 00000000..44ec7d69
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsTxField.java
@@ -0,0 +1,48 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class UsTxField {
+
+ public static String VERSION = "Version";
+ public static String PROCESSING_NOTICE = "ProcessingNotice";
+ public static String SALE_OPT_OUT_NOTICE = "SaleOptOutNotice";
+ public static String TARGETED_ADVERTISING_OPT_OUT_NOTICE = "TargetedAdvertisingOptOutNotice";
+ public static String SALE_OPT_OUT = "SaleOptOut";
+ public static String TARGETED_ADVERTISING_OPT_OUT = "TargetedAdvertisingOptOut";
+ public static String SENSITIVE_DATA_PROCESSING = "SensitiveDataProcessing";
+ public static String KNOWN_CHILD_SENSITIVE_DATA_CONSENTS = "KnownChildSensitiveDataConsents";
+ public static String ADDITIONAL_DATA_PROCESSING_CONSENT = "AdditionalDataProcessingConsent";
+ public static String MSPA_COVERED_TRANSACTION = "MspaCoveredTransaction";
+ public static String MSPA_OPT_OUT_OPTION_MODE = "MspaOptOutOptionMode";
+ public static String MSPA_SERVICE_PROVIDER_MODE = "MspaServiceProviderMode";
+
+ public static String GPC_SEGMENT_TYPE = "GpcSegmentType";
+ public static String GPC_SEGMENT_INCLUDED = "GpcSegmentIncluded";
+ public static String GPC = "Gpc";
+
+ //@formatter:off
+ public static List USTX_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsTxField.VERSION,
+ UsTxField.PROCESSING_NOTICE,
+ UsTxField.SALE_OPT_OUT_NOTICE,
+ UsTxField.TARGETED_ADVERTISING_OPT_OUT_NOTICE,
+ UsTxField.SALE_OPT_OUT,
+ UsTxField.TARGETED_ADVERTISING_OPT_OUT,
+ UsTxField.SENSITIVE_DATA_PROCESSING,
+ UsTxField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS,
+ UsTxField.ADDITIONAL_DATA_PROCESSING_CONSENT,
+ UsTxField.MSPA_COVERED_TRANSACTION,
+ UsTxField.MSPA_OPT_OUT_OPTION_MODE,
+ UsTxField.MSPA_SERVICE_PROVIDER_MODE
+ });
+ //@formatter:on
+
+ //@formatter:off
+ public static List USTX_GPC_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsTxField.GPC_SEGMENT_TYPE,
+ UsTxField.GPC
+ });
+ //@formatter:on
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsUtField.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsUtField.java
new file mode 100644
index 00000000..01cedc67
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsUtField.java
@@ -0,0 +1,37 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class UsUtField {
+
+ public static String VERSION = "Version";
+ public static String SHARING_NOTICE = "SharingNotice";
+ public static String SALE_OPT_OUT_NOTICE = "SaleOptOutNotice";
+ public static String TARGETED_ADVERTISING_OPT_OUT_NOTICE = "TargetedAdvertisingOptOutNotice";
+ public static String SENSITIVE_DATA_PROCESSING_OPT_OUT_NOTICE = "SensitiveDataProcessingOptOutNotice";
+ public static String SALE_OPT_OUT = "SaleOptOut";
+ public static String TARGETED_ADVERTISING_OPT_OUT = "TargetedAdvertisingOptOut";
+ public static String SENSITIVE_DATA_PROCESSING = "SensitiveDataProcessing";
+ public static String KNOWN_CHILD_SENSITIVE_DATA_CONSENTS = "KnownChildSensitiveDataConsents";
+ public static String MSPA_COVERED_TRANSACTION = "MspaCoveredTransaction";
+ public static String MSPA_OPT_OUT_OPTION_MODE = "MspaOptOutOptionMode";
+ public static String MSPA_SERVICE_PROVIDER_MODE = "MspaServiceProviderMode";
+
+ //@formatter:off
+ public static List USUT_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsUtField.VERSION,
+ UsUtField.SHARING_NOTICE,
+ UsUtField.SALE_OPT_OUT_NOTICE,
+ UsUtField.TARGETED_ADVERTISING_OPT_OUT_NOTICE,
+ UsUtField.SENSITIVE_DATA_PROCESSING_OPT_OUT_NOTICE,
+ UsUtField.SALE_OPT_OUT,
+ UsUtField.TARGETED_ADVERTISING_OPT_OUT,
+ UsUtField.SENSITIVE_DATA_PROCESSING,
+ UsUtField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS,
+ UsUtField.MSPA_COVERED_TRANSACTION,
+ UsUtField.MSPA_OPT_OUT_OPTION_MODE,
+ UsUtField.MSPA_SERVICE_PROVIDER_MODE
+ });
+ //@formatter:on
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsVaField.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsVaField.java
new file mode 100644
index 00000000..46cb4dbb
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UsVaField.java
@@ -0,0 +1,35 @@
+package com.iab.gpp.encoder.field;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class UsVaField {
+
+ public static String VERSION = "Version";
+ public static String SHARING_NOTICE = "SharingNotice";
+ public static String SALE_OPT_OUT_NOTICE = "SaleOptOutNotice";
+ public static String TARGETED_ADVERTISING_OPT_OUT_NOTICE = "TargetedAdvertisingOptOutNotice";
+ public static String SALE_OPT_OUT = "SaleOptOut";
+ public static String TARGETED_ADVERTISING_OPT_OUT = "TargetedAdvertisingOptOut";
+ public static String SENSITIVE_DATA_PROCESSING = "SensitiveDataProcessing";
+ public static String KNOWN_CHILD_SENSITIVE_DATA_CONSENTS = "KnownChildSensitiveDataConsents";
+ public static String MSPA_COVERED_TRANSACTION = "MspaCoveredTransaction";
+ public static String MSPA_OPT_OUT_OPTION_MODE = "MspaOptOutOptionMode";
+ public static String MSPA_SERVICE_PROVIDER_MODE = "MspaServiceProviderMode";
+
+ //@formatter:off
+ public static List USVA_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UsVaField.VERSION,
+ UsVaField.SHARING_NOTICE,
+ UsVaField.SALE_OPT_OUT_NOTICE,
+ UsVaField.TARGETED_ADVERTISING_OPT_OUT_NOTICE,
+ UsVaField.SALE_OPT_OUT,
+ UsVaField.TARGETED_ADVERTISING_OPT_OUT,
+ UsVaField.SENSITIVE_DATA_PROCESSING,
+ UsVaField.KNOWN_CHILD_SENSITIVE_DATA_CONSENTS,
+ UsVaField.MSPA_COVERED_TRANSACTION,
+ UsVaField.MSPA_OPT_OUT_OPTION_MODE,
+ UsVaField.MSPA_SERVICE_PROVIDER_MODE
+ });
+ //@formatter:on
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UspV1Field.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UspV1Field.java
index c5346823..2551632c 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UspV1Field.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UspV1Field.java
@@ -1,5 +1,8 @@
package com.iab.gpp.encoder.field;
+import java.util.Arrays;
+import java.util.List;
+
public class UspV1Field {
public static String VERSION = "Version";
@@ -7,4 +10,12 @@ public class UspV1Field {
public static String OPT_OUT_SALE = "OptOutSale";
public static String LSPA_COVERED = "LspaCovered";
+ //@formatter:off
+ public static List USPV1_CORE_SEGMENT_FIELD_NAMES = Arrays.asList(new String[] {
+ UspV1Field.VERSION,
+ UspV1Field.NOTICE,
+ UspV1Field.OPT_OUT_SALE,
+ UspV1Field.LSPA_COVERED
+ });
+ //@formatter:on
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UspV1LegacyField.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UspV1LegacyField.java
deleted file mode 100644
index 3b1642c1..00000000
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/field/UspV1LegacyField.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.iab.gpp.encoder.field;
-
-public class UspV1LegacyField {
-
- public static String VERSION = "Version";
- public static String NOTICE = "Notice";
- public static String OPT_OUT_SALE = "OptOutSale";
- public static String LSPA_COVERED = "LspaCovered";
-
-}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/AbstractEncodableBitStringSection.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/AbstractEncodableBitStringSection.java
deleted file mode 100644
index e5648f6c..00000000
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/AbstractEncodableBitStringSection.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.iab.gpp.encoder.section;
-
-import java.util.Map;
-import com.iab.gpp.encoder.error.DecodingException;
-import com.iab.gpp.encoder.error.EncodingException;
-import com.iab.gpp.encoder.datatype.AbstractEncodableBitStringDataType;
-
-public abstract class AbstractEncodableBitStringSection implements EncodableSection {
- protected Map> fields;
- protected String[] fieldOrder;
-
- @Override
- public boolean hasField(String fieldName) {
- return this.fields.containsKey(fieldName);
- }
-
- @Override
- public Object getFieldValue(String fieldName) {
- if (this.fields.containsKey(fieldName)) {
- return this.fields.get(fieldName).getValue();
- } else {
- return null;
- }
- }
-
- @Override
- public void setFieldValue(String fieldName, Object value) {
- if (this.fields.containsKey(fieldName)) {
- this.fields.get(fieldName).setValue(value);
- } else {
- throw new Error(fieldName + " not found");
- }
- }
-
- public String[] getFieldOrder() {
- return this.fieldOrder;
- }
-
- public String encodeToBitString() throws EncodingException {
- String bitString = "";
- for (int i = 0; i < this.fieldOrder.length; i++) {
- String fieldName = this.fieldOrder[i];
- if (this.fields.containsKey(fieldName)) {
- AbstractEncodableBitStringDataType> field = this.fields.get(fieldName);
- bitString += field.encode();
- } else {
- throw new Error("Field not found: '" + fieldName + "'");
- }
- }
-
- return bitString;
- }
-
- public void decodeFromBitString(String bitString) throws DecodingException {
- int index = 0;
- for (int i = 0; i < this.fieldOrder.length; i++) {
- String fieldName = this.fieldOrder[i];
- if (this.fields.containsKey(fieldName)) {
- AbstractEncodableBitStringDataType> field = this.fields.get(fieldName);
- String substring = field.substring(bitString, index);
- field.decode(substring);
- index += substring.length();
- } else {
- throw new Error("Field not found: '" + fieldName + "'");
- }
- }
- }
-
- @Override
- public abstract String encode() throws EncodingException;
-
- @Override
- public abstract void decode(String encodedString) throws DecodingException;
-
- @Override
- public abstract int getId();
-
- @Override
- public abstract String getName();
-}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/AbstractEncodableSegmentedBitStringSection.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/AbstractEncodableSegmentedBitStringSection.java
index 2b796fbf..eb5d5459 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/AbstractEncodableSegmentedBitStringSection.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/AbstractEncodableSegmentedBitStringSection.java
@@ -3,9 +3,11 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import com.iab.gpp.encoder.datatype.AbstractEncodableBitStringDataType;
+import com.iab.gpp.encoder.datatype.SubstringException;
import com.iab.gpp.encoder.error.DecodingException;
import com.iab.gpp.encoder.error.EncodingException;
-import com.iab.gpp.encoder.datatype.AbstractEncodableBitStringDataType;
+import com.iab.gpp.encoder.error.InvalidFieldException;
public abstract class AbstractEncodableSegmentedBitStringSection implements EncodableSection {
protected Map> fields;
@@ -26,11 +28,11 @@ public Object getFieldValue(String fieldName) {
}
@Override
- public void setFieldValue(String fieldName, Object value) {
+ public void setFieldValue(String fieldName, Object value) throws InvalidFieldException {
if (this.fields.containsKey(fieldName)) {
this.fields.get(fieldName).setValue(value);
} else {
- throw new Error(fieldName + " not found");
+ throw new InvalidFieldException(fieldName + " not found");
}
}
@@ -45,10 +47,14 @@ public List encodeSegmentsToBitStrings() throws EncodingException {
for (int j = 0; j < this.segments[i].length; j++) {
String fieldName = this.segments[i][j];
if (this.fields.containsKey(fieldName)) {
- AbstractEncodableBitStringDataType> field = this.fields.get(fieldName);
- segmentBitString += field.encode();
+ try {
+ AbstractEncodableBitStringDataType> field = this.fields.get(fieldName);
+ segmentBitString += field.encode();
+ } catch (Exception e) {
+ throw new EncodingException("Unable to encode " + fieldName, e);
+ }
} else {
- throw new Error("Field not found: '" + fieldName + "'");
+ throw new EncodingException("Field not found: '" + fieldName + "'");
}
}
segmentBitStrings.add(segmentBitString);
@@ -59,24 +65,37 @@ public List encodeSegmentsToBitStrings() throws EncodingException {
public void decodeSegmentsFromBitStrings(List segmentBitStrings) throws DecodingException {
for (int i = 0; i < this.segments.length && i < segmentBitStrings.size(); i++) {
- String segmentBitString = segmentBitStrings.get(i);
- if (segmentBitString != null && segmentBitString.length() > 0) {
- int index = 0;
- for (int j = 0; j < this.segments[i].length; j++) {
- String fieldName = this.segments[i][j];
- if (this.fields.containsKey(fieldName)) {
- AbstractEncodableBitStringDataType> field = this.fields.get(fieldName);
+ decodeSegmentFromBitString(segments[i], segmentBitStrings.get(i));
+ }
+ }
+
+ private void decodeSegmentFromBitString(String[] segment, String segmentBitString) throws DecodingException {
+ if (segmentBitString != null && segmentBitString.length() > 0) {
+ int index = 0;
+ for (int j = 0; j < segment.length; j++) {
+ String fieldName = segment[j];
+ AbstractEncodableBitStringDataType> field = this.fields.get(fieldName);
+ if (this.fields.containsKey(fieldName)) {
+ try {
String substring = field.substring(segmentBitString, index);
field.decode(substring);
index += substring.length();
- } else {
- throw new Error("Field not found: '" + fieldName + "'");
+ } catch (SubstringException e) {
+ if(field.getHardFailIfMissing()) {
+ throw new DecodingException("Unable to decode " + fieldName, e);
+ } else {
+ return;
+ }
+ } catch (Exception e) {
+ throw new DecodingException("Unable to decode " + fieldName, e);
}
+ } else {
+ throw new DecodingException("Field not found: '" + fieldName + "'");
}
}
}
}
-
+
@Override
public abstract String encode() throws EncodingException;
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/AbstractLazilyEncodableSection.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/AbstractLazilyEncodableSection.java
new file mode 100644
index 00000000..1f31a592
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/AbstractLazilyEncodableSection.java
@@ -0,0 +1,92 @@
+package com.iab.gpp.encoder.section;
+
+import java.util.List;
+import com.iab.gpp.encoder.error.InvalidFieldException;
+import com.iab.gpp.encoder.segment.EncodableSegment;
+
+public abstract class AbstractLazilyEncodableSection implements EncodableSection {
+
+ private List segments;
+
+ private String encodedString = null;
+
+ private boolean dirty = false;
+ private boolean decoded = true;
+
+ public AbstractLazilyEncodableSection() {
+ this.segments = initializeSegments();
+ }
+
+ protected abstract List initializeSegments();
+
+ protected abstract String encodeSection(List segments);
+
+ protected abstract List decodeSection(String encodedString);
+
+ public boolean hasField(String fieldName) {
+ if (!this.decoded) {
+ this.segments = this.decodeSection(this.encodedString);
+ this.dirty = false;
+ this.decoded = true;
+ }
+
+ for(EncodableSegment segment : segments) {
+ if(segment.getFieldNames().contains(fieldName)) {
+ return segment.hasField(fieldName);
+ }
+ }
+
+ return false;
+ }
+
+ public Object getFieldValue(String fieldName) {
+ if (!this.decoded) {
+ this.segments = this.decodeSection(this.encodedString);
+ this.dirty = false;
+ this.decoded = true;
+ }
+
+ for(EncodableSegment segment : segments) {
+ if(segment.hasField(fieldName)) {
+ return segment.getFieldValue(fieldName);
+ }
+ }
+
+ throw new InvalidFieldException("Invalid field: '" + fieldName + "'");
+ }
+
+ public void setFieldValue(String fieldName, Object value) {
+ if (!this.decoded) {
+ this.segments = this.decodeSection(this.encodedString);
+ this.dirty = false;
+ this.decoded = true;
+ }
+
+ for(EncodableSegment segment : segments) {
+ if(segment.hasField(fieldName)) {
+ segment.setFieldValue(fieldName, value);
+ this.dirty = true;
+ return;
+ }
+ }
+
+ throw new InvalidFieldException("Invalid field: '" + fieldName + "'");
+ }
+
+ public String encode() {
+ if (this.encodedString == null || this.encodedString.isEmpty() || this.dirty) {
+ this.encodedString = this.encodeSection(this.segments);
+ this.dirty = false;
+ this.decoded = true;
+ }
+
+ return this.encodedString;
+ }
+
+ public void decode(String encodedString) {
+ this.encodedString = encodedString;
+ this.dirty = false;
+ this.decoded = false;
+ }
+
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/EncodableSection.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/EncodableSection.java
index 9891c5a6..e1f0a854 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/EncodableSection.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/EncodableSection.java
@@ -1,21 +1,20 @@
package com.iab.gpp.encoder.section;
-import com.iab.gpp.encoder.error.DecodingException;
-import com.iab.gpp.encoder.error.EncodingException;
-
public interface EncodableSection {
int getId();
String getName();
+ int getVersion();
+
boolean hasField(String fieldName);
Object getFieldValue(String fieldName);
void setFieldValue(String fieldName, Object value);
- String encode() throws EncodingException;
+ String encode();
- void decode(String encodedString) throws DecodingException;
+ void decode(String encodedString);
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/HeaderV1.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/HeaderV1.java
index cd2d9bf8..fd5d991c 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/HeaderV1.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/HeaderV1.java
@@ -1,76 +1,79 @@
package com.iab.gpp.encoder.section;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import com.iab.gpp.encoder.datatype.encoder.Base64UrlEncoder;
-import com.iab.gpp.encoder.error.DecodingException;
-import com.iab.gpp.encoder.error.EncodingException;
-import com.iab.gpp.encoder.datatype.EncodableFibonacciIntegerRange;
-import com.iab.gpp.encoder.datatype.EncodableFixedInteger;
import com.iab.gpp.encoder.field.HeaderV1Field;
+import com.iab.gpp.encoder.segment.EncodableSegment;
+import com.iab.gpp.encoder.segment.HeaderV1CoreSegment;
-public class HeaderV1 extends AbstractEncodableBitStringSection {
+public class HeaderV1 extends AbstractLazilyEncodableSection {
+
public static int ID = 3;
public static int VERSION = 1;
public static String NAME = "header";
public HeaderV1() {
- initFields();
+ super();
}
- public HeaderV1(String encodedString) throws DecodingException {
- initFields();
-
- if (encodedString != null && encodedString.length() > 0) {
- this.decode(encodedString);
- }
+ public HeaderV1(String encodedString) {
+ super();
+ decode(encodedString);
}
- private void initFields() {
- fields = new HashMap<>();
- fields.put(HeaderV1Field.ID, new EncodableFixedInteger(6, HeaderV1.ID));
- fields.put(HeaderV1Field.VERSION, new EncodableFixedInteger(6, HeaderV1.VERSION));
- fields.put(HeaderV1Field.SECTION_IDS, new EncodableFibonacciIntegerRange(new ArrayList<>()));
-
- //@formatter:off
- fieldOrder = new String[] {
- HeaderV1Field.ID,
- HeaderV1Field.VERSION,
- HeaderV1Field.SECTION_IDS
- };
- //@formatter:on
+ @Override
+ public int getId() {
+ return HeaderV1.ID;
}
@Override
- public String encode() throws EncodingException {
- String bitString = this.encodeToBitString();
- String encodedString = Base64UrlEncoder.encode(bitString);
- return encodedString;
+ public String getName() {
+ return HeaderV1.NAME;
}
@Override
- public void decode(String encodedString) throws DecodingException {
- String bitString = Base64UrlEncoder.decode(encodedString);
- this.decodeFromBitString(bitString);
+ public int getVersion() {
+ return HeaderV1.VERSION;
}
@Override
- public int getId() {
- return HeaderV1.ID;
+ protected List initializeSegments() {
+ List segments = new ArrayList<>();
+ segments.add(new HeaderV1CoreSegment());
+ return segments;
}
-
+
@Override
- public String getName() {
- return HeaderV1.NAME;
+ protected List decodeSection(String encodedString) {
+ List segments = initializeSegments();
+
+ if(encodedString != null && !encodedString.isEmpty()) {
+ String[] encodedSegments = encodedString.split("\\.");
+
+ for(int i=0; i i) {
+ segments.get(i).decode(encodedSegments[i]);
+ }
+ }
+ }
+
+ return segments;
}
- public Integer getVersion() {
- return (Integer) this.fields.get(HeaderV1Field.VERSION).getValue();
+ @Override
+ protected String encodeSection(List segments) {
+ List encodedSegments = new ArrayList<>();
+ for(EncodableSegment segment : segments) {
+ encodedSegments.add(segment.encode());
+ }
+ return String.join(".", encodedSegments);
}
+
@SuppressWarnings("unchecked")
public List getSectionsIds() {
- return (List) this.fields.get(HeaderV1Field.SECTION_IDS).getValue();
+ return (List) this.getFieldValue(HeaderV1Field.SECTION_IDS);
}
+
+
}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/Sections.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/Sections.java
new file mode 100644
index 00000000..976805d3
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/Sections.java
@@ -0,0 +1,41 @@
+package com.iab.gpp.encoder.section;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class Sections {
+
+ public static List SECTION_ORDER;
+
+ public static Map SECTION_ID_NAME_MAP;
+
+ static {
+ SECTION_ID_NAME_MAP = new HashMap<>();
+
+ SECTION_ID_NAME_MAP.put(TcfEuV2.ID, TcfEuV2.NAME);
+ SECTION_ID_NAME_MAP.put(TcfCaV1.ID, TcfCaV1.NAME);
+ SECTION_ID_NAME_MAP.put(UspV1.ID, UspV1.NAME);
+ SECTION_ID_NAME_MAP.put(UsNat.ID, UsNat.NAME);
+ SECTION_ID_NAME_MAP.put(UsCa.ID, UsCa.NAME);
+ SECTION_ID_NAME_MAP.put(UsVa.ID, UsVa.NAME);
+ SECTION_ID_NAME_MAP.put(UsCo.ID, UsCo.NAME);
+ SECTION_ID_NAME_MAP.put(UsUt.ID, UsUt.NAME);
+ SECTION_ID_NAME_MAP.put(UsCt.ID, UsCt.NAME);
+ SECTION_ID_NAME_MAP.put(UsFl.ID, UsFl.NAME);
+ SECTION_ID_NAME_MAP.put(UsMt.ID, UsMt.NAME);
+ SECTION_ID_NAME_MAP.put(UsOr.ID, UsOr.NAME);
+ SECTION_ID_NAME_MAP.put(UsTx.ID, UsTx.NAME);
+ SECTION_ID_NAME_MAP.put(UsDe.ID, UsDe.NAME);
+ SECTION_ID_NAME_MAP.put(UsIa.ID, UsIa.NAME);
+ SECTION_ID_NAME_MAP.put(UsNe.ID, UsNe.NAME);
+ SECTION_ID_NAME_MAP.put(UsNh.ID, UsNh.NAME);
+ SECTION_ID_NAME_MAP.put(UsNj.ID, UsNj.NAME);
+ SECTION_ID_NAME_MAP.put(UsTn.ID, UsTn.NAME);
+
+ SECTION_ORDER = new ArrayList(SECTION_ID_NAME_MAP.keySet()).stream().sorted()
+ .map(id -> SECTION_ID_NAME_MAP.get(id)).collect(Collectors.toList());
+ }
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/TcfCaV1.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/TcfCaV1.java
new file mode 100644
index 00000000..194a82dc
--- /dev/null
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/TcfCaV1.java
@@ -0,0 +1,223 @@
+package com.iab.gpp.encoder.section;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import com.iab.gpp.encoder.datatype.RangeEntry;
+import com.iab.gpp.encoder.error.DecodingException;
+import com.iab.gpp.encoder.error.InvalidFieldException;
+import com.iab.gpp.encoder.field.TcfCaV1Field;
+import com.iab.gpp.encoder.segment.EncodableSegment;
+import com.iab.gpp.encoder.segment.TcfCaV1CoreSegment;
+import com.iab.gpp.encoder.segment.TcfCaV1DisclosedVendorsSegment;
+import com.iab.gpp.encoder.segment.TcfCaV1PublisherPurposesSegment;
+
+public class TcfCaV1 extends AbstractLazilyEncodableSection {
+
+ public static int ID = 5;
+ public static int VERSION = 1;
+ public static String NAME = "tcfcav1";
+
+ public TcfCaV1() {
+ super();
+ }
+
+ public TcfCaV1(String encodedString) {
+ super();
+ decode(encodedString);
+ }
+
+ @Override
+ public int getId() {
+ return TcfCaV1.ID;
+ }
+
+ @Override
+ public String getName() {
+ return TcfCaV1.NAME;
+ }
+
+ @Override
+ public int getVersion() {
+ return TcfCaV1.VERSION;
+ }
+
+ @Override
+ protected List initializeSegments() {
+ List segments = new ArrayList<>();
+ segments.add(new TcfCaV1CoreSegment());
+ segments.add(new TcfCaV1PublisherPurposesSegment());
+ segments.add(new TcfCaV1DisclosedVendorsSegment());
+ return segments;
+ }
+
+ @Override
+ public List decodeSection(String encodedString) {
+ List segments = initializeSegments();
+
+ if(encodedString != null && !encodedString.isEmpty()) {
+ String[] encodedSegments = encodedString.split("\\.");
+ for (int i = 0; i < encodedSegments.length; i++) {
+
+ /**
+ * The first 3 bits contain the segment id. Rather than decode the entire string, just check the first character.
+ *
+ * A-H = '000' = 0
+ * I-P = '001' = 1
+ * Y-Z,a-f = '011' = 3
+ *
+ * Note that there is no segment id field for the core segment. Instead the first 6 bits are reserved
+ * for the encoding version which only coincidentally works here because the version value is less than 8.
+ */
+
+ String encodedSegment = encodedSegments[i];
+ if(!encodedSegment.isEmpty()) {
+ char firstChar = encodedSegment.charAt(0);
+
+ if(firstChar >= 'A' && firstChar <= 'H') {
+ segments.get(0).decode(encodedSegments[i]);
+ } else if(firstChar >= 'I' && firstChar <= 'P') {
+ segments.get(2).decode(encodedSegments[i]);
+ } else if((firstChar >= 'Y' && firstChar <= 'Z') || (firstChar >= 'a' && firstChar <= 'f')) {
+ segments.get(1).decode(encodedSegments[i]);
+ } else {
+ throw new DecodingException("Invalid segment '" + encodedSegment + "'");
+ }
+ }
+ }
+ }
+
+ return segments;
+ }
+
+ @Override
+ public String encodeSection(List segments) {
+ List encodedSegments = new ArrayList<>();
+
+ encodedSegments.add(segments.get(0).encode());
+ encodedSegments.add(segments.get(1).encode());
+ if(!this.getDisclosedVendors().isEmpty()) {
+ encodedSegments.add(segments.get(2).encode());
+ }
+
+ return String.join(".", encodedSegments);
+ }
+
+ @Override
+ public void setFieldValue(String fieldName, Object value) throws InvalidFieldException {
+ super.setFieldValue(fieldName, value);
+
+ if (!fieldName.equals(TcfCaV1Field.CREATED) && !fieldName.equals(TcfCaV1Field.LAST_UPDATED)) {
+ ZonedDateTime utcDateTime = ZonedDateTime.now(ZoneId.of("UTC"));
+
+ super.setFieldValue(TcfCaV1Field.CREATED, utcDateTime);
+ super.setFieldValue(TcfCaV1Field.LAST_UPDATED, utcDateTime);
+ }
+ }
+
+
+ public ZonedDateTime getCreated() {
+ return (ZonedDateTime) this.getFieldValue(TcfCaV1Field.CREATED);
+ }
+
+ public ZonedDateTime getLastUpdated() {
+ return (ZonedDateTime) this.getFieldValue(TcfCaV1Field.LAST_UPDATED);
+ }
+
+ public Integer getCmpId() {
+ return (Integer) this.getFieldValue(TcfCaV1Field.CMP_ID);
+ }
+
+ public Integer getCmpVersion() {
+ return (Integer) this.getFieldValue(TcfCaV1Field.CMP_VERSION);
+ }
+
+ public Integer getConsentScreen() {
+ return (Integer) this.getFieldValue(TcfCaV1Field.CONSENT_SCREEN);
+ }
+
+ public String getConsentLanguage() {
+ return (String) this.getFieldValue(TcfCaV1Field.CONSENT_LANGUAGE);
+ }
+
+ public Integer getVendorListVersion() {
+ return (Integer) this.getFieldValue(TcfCaV1Field.VENDOR_LIST_VERSION);
+ }
+
+ public Integer getPolicyVersion() {
+ return (Integer) this.getFieldValue(TcfCaV1Field.TCF_POLICY_VERSION);
+ }
+
+ public Boolean getUseNonStandardStacks() {
+ return (Boolean) this.getFieldValue(TcfCaV1Field.USE_NON_STANDARD_STACKS);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List getSpecialFeatureExpressConsent() {
+ return (List) this.getFieldValue(TcfCaV1Field.SPECIAL_FEATURE_EXPRESS_CONSENT);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List getPurposesExpressConsent() {
+ return (List) this.getFieldValue(TcfCaV1Field.PURPOSES_EXPRESS_CONSENT);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List getPurposesImpliedConsent() {
+ return (List) this.getFieldValue(TcfCaV1Field.PURPOSES_IMPLIED_CONSENT);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List getVendorExpressConsent() {
+ return (List) this.getFieldValue(TcfCaV1Field.VENDOR_EXPRESS_CONSENT);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List getVendorImpliedConsent() {
+ return (List) this.getFieldValue(TcfCaV1Field.VENDOR_IMPLIED_CONSENT);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List getPubRestrictions() {
+ return (List) this.getFieldValue(TcfCaV1Field.PUB_RESTRICTIONS);
+ }
+
+ public Integer getPubPurposesSegmentType() {
+ return (Integer) this.getFieldValue(TcfCaV1Field.PUB_PURPOSES_SEGMENT_TYPE);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List getPubPurposesExpressConsent() {
+ return (List) this.getFieldValue(TcfCaV1Field.PUB_PURPOSES_EXPRESS_CONSENT);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List getPubPurposesImpliedConsent() {
+ return (List) this.getFieldValue(TcfCaV1Field.PUB_PURPOSES_IMPLIED_CONSENT);
+ }
+
+ public Integer getNumCustomPurposes() {
+ return (Integer) this.getFieldValue(TcfCaV1Field.NUM_CUSTOM_PURPOSES);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List getCustomPurposesExpressConsent() {
+ return (List) this.getFieldValue(TcfCaV1Field.CUSTOM_PURPOSES_EXPRESS_CONSENT);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List getCustomPurposesImpliedConsent() {
+ return (List) this.getFieldValue(TcfCaV1Field.CUSTOM_PURPOSES_IMPLIED_CONSENT);
+ }
+
+ public Integer getDisclosedVendorsSegmentType() {
+ return (Integer) this.getFieldValue(TcfCaV1Field.DISCLOSED_VENDORS_SEGMENT_TYPE);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List getDisclosedVendors() {
+ return (List) this.getFieldValue(TcfCaV1Field.DISCLOSED_VENDORS);
+ }
+
+}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/TcfCaV2.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/TcfCaV2.java
deleted file mode 100644
index a5c27280..00000000
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/TcfCaV2.java
+++ /dev/null
@@ -1,278 +0,0 @@
-package com.iab.gpp.encoder.section;
-
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.function.IntSupplier;
-import java.util.stream.Collectors;
-import com.iab.gpp.encoder.datatype.EncodableBoolean;
-import com.iab.gpp.encoder.datatype.EncodableDatetime;
-import com.iab.gpp.encoder.datatype.EncodableFixedBitfield;
-import com.iab.gpp.encoder.datatype.EncodableFixedInteger;
-import com.iab.gpp.encoder.datatype.EncodableFixedIntegerRange;
-import com.iab.gpp.encoder.datatype.EncodableFixedString;
-import com.iab.gpp.encoder.datatype.EncodableFlexibleBitfield;
-import com.iab.gpp.encoder.datatype.encoder.Base64UrlEncoder;
-import com.iab.gpp.encoder.error.DecodingException;
-import com.iab.gpp.encoder.error.EncodingException;
-import com.iab.gpp.encoder.field.TcfCaV2Field;
-
-public class TcfCaV2 extends AbstractEncodableSegmentedBitStringSection {
- public static int ID = 5;
- public static int VERSION = 2;
- public static String NAME = "tcfcav2";
-
- public TcfCaV2() {
- initFields();
- }
-
- public TcfCaV2(String encodedString) throws DecodingException {
- initFields();
-
- if (encodedString != null && encodedString.length() > 0) {
- this.decode(encodedString);
- }
- }
-
- private void initFields() {
- fields = new HashMap<>();
-
- // core section
- fields.put(TcfCaV2Field.VERSION, new EncodableFixedInteger(6, TcfCaV2.VERSION));
- fields.put(TcfCaV2Field.CREATED, new EncodableDatetime());
- fields.put(TcfCaV2Field.LAST_UPDATED, new EncodableDatetime());
- fields.put(TcfCaV2Field.CMP_ID, new EncodableFixedInteger(12, 0));
- fields.put(TcfCaV2Field.CMP_VERSION, new EncodableFixedInteger(12, 0));
- fields.put(TcfCaV2Field.CONSENT_SCREEN, new EncodableFixedInteger(6, 0));
- fields.put(TcfCaV2Field.CONSENT_LANGUAGE, new EncodableFixedString(2, "EN"));
- fields.put(TcfCaV2Field.VENDOR_LIST_VERSION, new EncodableFixedInteger(12, 0));
- fields.put(TcfCaV2Field.TCF_POLICY_VERSION, new EncodableFixedInteger(6, 2));
- fields.put(TcfCaV2Field.USE_NON_STANDARD_STACKS, new EncodableBoolean(false));
- fields.put(TcfCaV2Field.SPECIAL_FEATURE_EXPRESS_CONSENT, new EncodableFixedBitfield(12, new ArrayList<>()));
- fields.put(TcfCaV2Field.PURPOSES_EXPRESS_CONSENT, new EncodableFixedBitfield(24, new ArrayList<>()));
- fields.put(TcfCaV2Field.PURPOSES_IMPLIED_CONSENT, new EncodableFixedBitfield(24, new ArrayList<>()));
- fields.put(TcfCaV2Field.VENDOR_EXPRESS_CONSENT, new EncodableFixedIntegerRange(new ArrayList<>()));
- fields.put(TcfCaV2Field.VENDOR_IMPLIED_CONSENT, new EncodableFixedIntegerRange(new ArrayList<>()));
-
- // publisher purposes segment
- fields.put(TcfCaV2Field.SEGMENT_TYPE, new EncodableFixedInteger(3, 3));
- fields.put(TcfCaV2Field.PUB_PURPOSES_EXPRESS_CONSENT, new EncodableFixedBitfield(24, new ArrayList<>()));
- fields.put(TcfCaV2Field.PUB_PURPOSES_IMPLIED_CONSENT, new EncodableFixedBitfield(24, new ArrayList<>()));
-
- EncodableFixedInteger numCustomPurposes = new EncodableFixedInteger(6, 0);
- fields.put(TcfCaV2Field.NUM_CUSTOM_PURPOSES, numCustomPurposes);
-
- IntSupplier getLengthSupplier = new IntSupplier() {
-
- @Override
- public int getAsInt() {
- return numCustomPurposes.getValue();
- }
-
- };
-
- fields.put(TcfCaV2Field.CUSTOM_PURPOSES_EXPRESS_CONSENT,
- new EncodableFlexibleBitfield(getLengthSupplier, new ArrayList<>()));
-
- fields.put(TcfCaV2Field.CUSTOM_PURPOSES_IMPLIED_CONSENT,
- new EncodableFlexibleBitfield(getLengthSupplier, new ArrayList<>()));
-
- //@formatter:off
- String[] coreSegment = new String[] {
- TcfCaV2Field.VERSION,
- TcfCaV2Field.CREATED,
- TcfCaV2Field.LAST_UPDATED,
- TcfCaV2Field.CMP_ID,
- TcfCaV2Field.CMP_VERSION,
- TcfCaV2Field.CONSENT_SCREEN,
- TcfCaV2Field.CONSENT_LANGUAGE,
- TcfCaV2Field.VENDOR_LIST_VERSION,
- TcfCaV2Field.TCF_POLICY_VERSION,
- TcfCaV2Field.USE_NON_STANDARD_STACKS,
- TcfCaV2Field.SPECIAL_FEATURE_EXPRESS_CONSENT,
- TcfCaV2Field.PURPOSES_EXPRESS_CONSENT,
- TcfCaV2Field.PURPOSES_IMPLIED_CONSENT,
- TcfCaV2Field.VENDOR_EXPRESS_CONSENT,
- TcfCaV2Field.VENDOR_IMPLIED_CONSENT
- };
-
- String[] publisherPurposesSegment = new String[] {
- TcfCaV2Field.SEGMENT_TYPE,
- TcfCaV2Field.PUB_PURPOSES_EXPRESS_CONSENT,
- TcfCaV2Field.PUB_PURPOSES_IMPLIED_CONSENT,
- TcfCaV2Field.NUM_CUSTOM_PURPOSES,
- TcfCaV2Field.CUSTOM_PURPOSES_EXPRESS_CONSENT,
- TcfCaV2Field.CUSTOM_PURPOSES_IMPLIED_CONSENT,
- };
-
- segments = new String[][] {
- coreSegment,
- publisherPurposesSegment
- };
- //@formatter:on
- }
-
- @Override
- public String encode() throws EncodingException {
- List segmentBitStrings = this.encodeSegmentsToBitStrings();
- List encodedSegments = new ArrayList<>();
- if (segmentBitStrings.size() >= 1) {
- encodedSegments.add(Base64UrlEncoder.encode(segmentBitStrings.get(0)));
-
- if (segmentBitStrings.size() >= 2) {
- encodedSegments.add(Base64UrlEncoder.encode(segmentBitStrings.get(1)));
- }
- }
-
- return encodedSegments.stream().collect(Collectors.joining("."));
- }
-
- @Override
- public void decode(String encodedSection) throws DecodingException {
- String[] encodedSegments = encodedSection.split("\\.");
- String[] segmentBitStrings = new String[4];
- for (int i = 0; i < encodedSegments.length; i++) {
- /**
- * first char will contain 6 bits, we only need the first 3. In version 1 and 2 of the TC string
- * there is no segment type for the CORE string. Instead the first 6 bits are reserved for the
- * encoding version, but because we're only on a maximum of encoding version 2 the first 3 bits in
- * the core segment will evaluate to 0.
- */
- String segmentBitString = Base64UrlEncoder.decode(encodedSegments[i]);
- switch (segmentBitString.substring(0, 3)) {
- // unfortunately, the segment ordering doesn't match the segment ids
- case "000": {
- segmentBitStrings[0] = segmentBitString;
- break;
- }
- case "011": {
- segmentBitStrings[1] = segmentBitString;
- break;
- }
- default: {
- throw new DecodingException("Unable to decode segment '" + encodedSegments[i] + "'");
- }
- }
- }
- this.decodeSegmentsFromBitStrings(Arrays.asList(segmentBitStrings));
- }
-
- @Override
- public void setFieldValue(String fieldName, Object value) {
- super.setFieldValue(fieldName, value);
-
- if (!fieldName.equals(TcfCaV2Field.CREATED) && !fieldName.equals(TcfCaV2Field.LAST_UPDATED)) {
- ZonedDateTime utcDateTime = ZonedDateTime.now(ZoneId.of("UTC"));
-
- super.setFieldValue(TcfCaV2Field.CREATED, utcDateTime);
- super.setFieldValue(TcfCaV2Field.LAST_UPDATED, utcDateTime);
- }
- }
-
- @Override
- public int getId() {
- return TcfCaV2.ID;
- }
-
- @Override
- public String getName() {
- return TcfCaV2.NAME;
- }
-
- public Integer getVersion() {
- return (Integer) this.fields.get(TcfCaV2Field.VERSION).getValue();
- }
-
- public ZonedDateTime getCreated() {
- return (ZonedDateTime) this.fields.get(TcfCaV2Field.CREATED).getValue();
- }
-
- public ZonedDateTime getLastUpdated() {
- return (ZonedDateTime) this.fields.get(TcfCaV2Field.LAST_UPDATED).getValue();
- }
-
- public Integer getCmpId() {
- return (Integer) this.fields.get(TcfCaV2Field.CMP_ID).getValue();
- }
-
- public Integer getCmpVersion() {
- return (Integer) this.fields.get(TcfCaV2Field.CMP_VERSION).getValue();
- }
-
- public Integer getConsentScreen() {
- return (Integer) this.fields.get(TcfCaV2Field.CONSENT_SCREEN).getValue();
- }
-
- public String getConsentLanguage() {
- return (String) this.fields.get(TcfCaV2Field.CONSENT_LANGUAGE).getValue();
- }
-
- public Integer getVendorListVersion() {
- return (Integer) this.fields.get(TcfCaV2Field.VENDOR_LIST_VERSION).getValue();
- }
-
- public Integer getPolicyVersion() {
- return (Integer) this.fields.get(TcfCaV2Field.TCF_POLICY_VERSION).getValue();
- }
-
- public Boolean getUseNonStandardStacks() {
- return (Boolean) this.fields.get(TcfCaV2Field.USE_NON_STANDARD_STACKS).getValue();
- }
-
- @SuppressWarnings("unchecked")
- public List getSpecialFeatureExpressConsent() {
- return (List) this.fields.get(TcfCaV2Field.SPECIAL_FEATURE_EXPRESS_CONSENT).getValue();
- }
-
- @SuppressWarnings("unchecked")
- public List getPurposesExpressConsent() {
- return (List) this.fields.get(TcfCaV2Field.PURPOSES_EXPRESS_CONSENT).getValue();
- }
-
- @SuppressWarnings("unchecked")
- public List getPurposesImpliedConsent() {
- return (List) this.fields.get(TcfCaV2Field.PURPOSES_IMPLIED_CONSENT).getValue();
- }
-
- @SuppressWarnings("unchecked")
- public List getVendorExpressConsent() {
- return (List) this.fields.get(TcfCaV2Field.VENDOR_EXPRESS_CONSENT).getValue();
- }
-
- @SuppressWarnings("unchecked")
- public List getVendorImpliedConsent() {
- return (List) this.fields.get(TcfCaV2Field.VENDOR_IMPLIED_CONSENT).getValue();
- }
-
- public Integer getSegmentType() {
- return (Integer) this.fields.get(TcfCaV2Field.SEGMENT_TYPE).getValue();
- }
-
- @SuppressWarnings("unchecked")
- public List getPubPurposesExpressConsent() {
- return (List) this.fields.get(TcfCaV2Field.PUB_PURPOSES_EXPRESS_CONSENT).getValue();
- }
-
- @SuppressWarnings("unchecked")
- public List getPubPurposesImpliedConsent() {
- return (List) this.fields.get(TcfCaV2Field.PUB_PURPOSES_IMPLIED_CONSENT).getValue();
- }
-
- public Integer getNumCustomPurposes() {
- return (Integer) this.fields.get(TcfCaV2Field.NUM_CUSTOM_PURPOSES).getValue();
- }
-
- @SuppressWarnings("unchecked")
- public List getCustomPurposesExpressConsent() {
- return (List) this.fields.get(TcfCaV2Field.CUSTOM_PURPOSES_EXPRESS_CONSENT).getValue();
- }
-
- @SuppressWarnings("unchecked")
- public List getCustomPurposesImpliedConsent() {
- return (List) this.fields.get(TcfCaV2Field.CUSTOM_PURPOSES_IMPLIED_CONSENT).getValue();
- }
-
-}
diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/TcfEuV2.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/TcfEuV2.java
index 498ae46b..1f7f2b1e 100644
--- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/TcfEuV2.java
+++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/section/TcfEuV2.java
@@ -3,212 +3,127 @@
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
import java.util.List;
-import java.util.function.IntSupplier;
-import java.util.stream.Collectors;
-import com.iab.gpp.encoder.datatype.encoder.Base64UrlEncoder;
+import com.iab.gpp.encoder.datatype.RangeEntry;
import com.iab.gpp.encoder.error.DecodingException;
-import com.iab.gpp.encoder.error.EncodingException;
-import com.iab.gpp.encoder.datatype.EncodableBoolean;
-import com.iab.gpp.encoder.datatype.EncodableDatetime;
-import com.iab.gpp.encoder.datatype.EncodableFixedBitfield;
-import com.iab.gpp.encoder.datatype.EncodableFixedInteger;
-import com.iab.gpp.encoder.datatype.EncodableFixedIntegerRange;
-import com.iab.gpp.encoder.datatype.EncodableFixedString;
-import com.iab.gpp.encoder.datatype.EncodableFlexibleBitfield;
-import com.iab.gpp.encoder.datatype.EncodableOptimizedFixedRange;
+import com.iab.gpp.encoder.error.InvalidFieldException;
import com.iab.gpp.encoder.field.TcfEuV2Field;
-
-public class TcfEuV2 extends AbstractEncodableSegmentedBitStringSection {
+import com.iab.gpp.encoder.segment.EncodableSegment;
+import com.iab.gpp.encoder.segment.TcfEuV2CoreSegment;
+import com.iab.gpp.encoder.segment.TcfEuV2PublisherPurposesSegment;
+import com.iab.gpp.encoder.segment.TcfEuV2VendorsAllowedSegment;
+import com.iab.gpp.encoder.segment.TcfEuV2VendorsDisclosedSegment;
+
+public class TcfEuV2 extends AbstractLazilyEncodableSection {
+
public static int ID = 2;
public static int VERSION = 2;
public static String NAME = "tcfeuv2";
public TcfEuV2() {
- initFields();
+ super();
}
- public TcfEuV2(String encodedString) throws DecodingException {
- initFields();
+ public TcfEuV2(String encodedString) {
+ super();
+ decode(encodedString);
+ }
- if (encodedString != null && encodedString.length() > 0) {
- this.decode(encodedString);
- }
+ @Override
+ public int getId() {
+ return TcfEuV2.ID;
}
- private void initFields() {
- fields = new HashMap<>();
-
- // core section
- fields.put(TcfEuV2Field.VERSION, new EncodableFixedInteger(6, TcfEuV2.VERSION));
- fields.put(TcfEuV2Field.CREATED, new EncodableDatetime());
- fields.put(TcfEuV2Field.LAST_UPDATED, new EncodableDatetime());
- fields.put(TcfEuV2Field.CMP_ID, new EncodableFixedInteger(12, 0));
- fields.put(TcfEuV2Field.CMP_VERSION, new EncodableFixedInteger(12, 0));
- fields.put(TcfEuV2Field.CONSENT_SCREEN, new EncodableFixedInteger(6, 0));
- fields.put(TcfEuV2Field.CONSENT_LANGUAGE, new EncodableFixedString(2, "EN"));
- fields.put(TcfEuV2Field.VENDOR_LIST_VERSION, new EncodableFixedInteger(12, 0));
- fields.put(TcfEuV2Field.POLICY_VERSION, new EncodableFixedInteger(6, 2));
- fields.put(TcfEuV2Field.IS_SERVICE_SPECIFIC, new EncodableBoolean(false));
- fields.put(TcfEuV2Field.USE_NON_STANDARD_STACKS, new EncodableBoolean(false));
- fields.put(TcfEuV2Field.SPECIAL_FEATURE_OPTINS, new EncodableFixedBitfield(12, new ArrayList<>()));
- fields.put(TcfEuV2Field.PURPOSE_CONSENTS, new EncodableFixedBitfield(24, new ArrayList<>()));
- fields.put(TcfEuV2Field.PURPOSE_LEGITIMATE_INTERESTS, new EncodableFixedBitfield(24, new ArrayList<>()));
- fields.put(TcfEuV2Field.PURPOSE_ONE_TREATMENT, new EncodableBoolean(false));
- fields.put(TcfEuV2Field.PUBLISHER_COUNTRY_CODE, new EncodableFixedString(2, "AA"));
- fields.put(TcfEuV2Field.VENDOR_CONSENTS, new EncodableFixedIntegerRange(new ArrayList<>()));
- fields.put(TcfEuV2Field.VENDOR_LEGITIMATE_INTERESTS, new EncodableFixedIntegerRange(new ArrayList<>()));
-
- fields.put(TcfEuV2Field.PUBLISHER_RESTRICTIONS, new EncodableFixedIntegerRange(new ArrayList<>()));
-
- // publisher purposes segment
- fields.put(TcfEuV2Field.PUBLISHER_PURPOSES_SEGMENT_TYPE, new EncodableFixedInteger(3, 3));
- fields.put(TcfEuV2Field.PUBLISHER_CONSENTS, new EncodableFixedBitfield(24, new ArrayList<>()));
- fields.put(TcfEuV2Field.PUBLISHER_LEGITIMATE_INTERESTS, new EncodableFixedBitfield(24, new ArrayList<>()));
-
- EncodableFixedInteger numCustomPurposes = new EncodableFixedInteger(6, 0);
- fields.put(TcfEuV2Field.NUM_CUSTOM_PURPOSES, numCustomPurposes);
-
- IntSupplier getLengthSupplier = new IntSupplier() {
-
- @Override
- public int getAsInt() {
- return numCustomPurposes.getValue();
- }
+ @Override
+ public String getName() {
+ return TcfEuV2.NAME;
+ }
- };
-
- fields.put(TcfEuV2Field.PUBLISHER_CUSTOM_CONSENTS,
- new EncodableFlexibleBitfield(getLengthSupplier, new ArrayList<>()));
-
- fields.put(TcfEuV2Field.PUBLISHER_CUSTOM_LEGITIMATE_INTERESTS,
- new EncodableFlexibleBitfield(getLengthSupplier, new ArrayList<>()));
-
- fields.put(TcfEuV2Field.VENDORS_ALLOWED_SEGMENT_TYPE, new EncodableFixedInteger(3, 2));
- fields.put(TcfEuV2Field.VENDORS_ALLOWED, new EncodableOptimizedFixedRange(new ArrayList<>()));
-
- fields.put(TcfEuV2Field.VENDORS_DISCLOSED_SEGMENT_TYPE, new EncodableFixedInteger(3, 1));
- fields.put(TcfEuV2Field.VENDORS_DISCLOSED, new EncodableOptimizedFixedRange(new ArrayList<>()));
-
- //@formatter:off
- String[] coreSegment = new String[] {
- TcfEuV2Field.VERSION,
- TcfEuV2Field.CREATED,
- TcfEuV2Field.LAST_UPDATED,
- TcfEuV2Field.CMP_ID,
- TcfEuV2Field.CMP_VERSION,
- TcfEuV2Field.CONSENT_SCREEN,
- TcfEuV2Field.CONSENT_LANGUAGE,
- TcfEuV2Field.VENDOR_LIST_VERSION,
- TcfEuV2Field.POLICY_VERSION,
- TcfEuV2Field.IS_SERVICE_SPECIFIC,
- TcfEuV2Field.USE_NON_STANDARD_STACKS,
- TcfEuV2Field.SPECIAL_FEATURE_OPTINS,
- TcfEuV2Field.PURPOSE_CONSENTS,
- TcfEuV2Field.PURPOSE_LEGITIMATE_INTERESTS,
- TcfEuV2Field.PURPOSE_ONE_TREATMENT,
- TcfEuV2Field.PUBLISHER_COUNTRY_CODE,
- TcfEuV2Field.VENDOR_CONSENTS,
- TcfEuV2Field.VENDOR_LEGITIMATE_INTERESTS,
- TcfEuV2Field.PUBLISHER_RESTRICTIONS
- };
-
- String[] publisherPurposesSegment = new String[] {
- TcfEuV2Field.PUBLISHER_PURPOSES_SEGMENT_TYPE,
- TcfEuV2Field.PUBLISHER_CONSENTS,
- TcfEuV2Field.PUBLISHER_LEGITIMATE_INTERESTS,
- TcfEuV2Field.NUM_CUSTOM_PURPOSES,
- TcfEuV2Field.PUBLISHER_CUSTOM_CONSENTS,
- TcfEuV2Field.PUBLISHER_CUSTOM_LEGITIMATE_INTERESTS,
- };
-
- String[] vendorsAllowedSegment = new String[] {
- TcfEuV2Field.VENDORS_ALLOWED_SEGMENT_TYPE,
- TcfEuV2Field.VENDORS_ALLOWED,
- };
-
- String[] vendorsDisclosedSegment = new String[] {
- TcfEuV2Field.VENDORS_DISCLOSED_SEGMENT_TYPE,
- TcfEuV2Field.VENDORS_DISCLOSED,
- };
-
- segments = new String[][] {
- coreSegment,
- publisherPurposesSegment,
- vendorsAllowedSegment,
- vendorsDisclosedSegment
- };
- //@formatter:on
+ @Override
+ public int getVersion() {
+ return TcfEuV2.VERSION;
}
@Override
- public String encode() throws EncodingException {
- List segmentBitStrings = this.encodeSegmentsToBitStrings();
+ protected List initializeSegments() {
+ List segments = new ArrayList<>();
+ segments.add(new TcfEuV2CoreSegment());
+ segments.add(new TcfEuV2PublisherPurposesSegment());
+ segments.add(new TcfEuV2VendorsAllowedSegment());
+ segments.add(new TcfEuV2VendorsDisclosedSegment());
+ return segments;
+ }
+
+ @Override
+ public List decodeSection(String encodedString) {
+ List segments = initializeSegments();
+
+ if(encodedString != null && !encodedString.isEmpty()) {
+ String[] encodedSegments = encodedString.split("\\.");
+ for (int i = 0; i < encodedSegments.length; i++) {
+
+ /**
+ * The first 3 bits contain the segment id. Rather than decode the entire string, just check the first character.
+ *
+ * A-H = '000' = 0
+ * I-P = '001' = 1
+ * Q-X = '010' = 2
+ * Y-Z,a-f = '011' = 3
+ *
+ * Note that there is no segment id field for the core segment. Instead the first 6 bits are reserved
+ * for the encoding version which only coincidentally works here because the version value is less than 8.
+ */
+
+ String encodedSegment = encodedSegments[i];
+ if(!encodedSegment.isEmpty()) {
+ char firstChar = encodedSegment.charAt(0);
+
+ // unfortunately, the segment ordering doesn't match the segment ids
+ if(firstChar >= 'A' && firstChar <= 'H') {
+ segments.get(0).decode(encodedSegments[i]);
+ } else if(firstChar >= 'I' && firstChar <= 'P') {
+ segments.get(3).decode(encodedSegments[i]);
+ } else if(firstChar >= 'Q' && firstChar <= 'X') {
+ segments.get(2).decode(encodedSegments[i]);
+ } else if((firstChar >= 'Y' && firstChar <= 'Z') || (firstChar >= 'a' && firstChar <= 'f')) {
+ segments.get(1).decode(encodedSegments[i]);
+ } else {
+ throw new DecodingException("Invalid segment '" + encodedSegment + "'");
+ }
+ }
+ }
+ }
+
+ return segments;
+ }
+
+ @Override
+ public String encodeSection(List segments) {
List encodedSegments = new ArrayList<>();
- if (segmentBitStrings.size() >= 1) {
- encodedSegments.add(Base64UrlEncoder.encode(segmentBitStrings.get(0)));
+ if (segments.size() >= 1) {
+ encodedSegments.add(segments.get(0).encode());
Boolean isServiceSpecific = (Boolean) this.getFieldValue(TcfEuV2Field.IS_SERVICE_SPECIFIC);
if (isServiceSpecific) {
- if (segmentBitStrings.size() >= 2) {
- encodedSegments.add(Base64UrlEncoder.encode(segmentBitStrings.get(1)));
+ if (segments.size() >= 2) {
+ encodedSegments.add(segments.get(1).encode());
}
} else {
- if (segmentBitStrings.size() >= 2) {
- encodedSegments.add(Base64UrlEncoder.encode(segmentBitStrings.get(2)));
+ if (segments.size() >= 2) {
+ encodedSegments.add(segments.get(2).encode());
- if (segmentBitStrings.size() >= 3) {
- encodedSegments.add(Base64UrlEncoder.encode(segmentBitStrings.get(3)));
+ if (segments.size() >= 3) {
+ encodedSegments.add(segments.get(3).encode());
}
}
}
}
- return encodedSegments.stream().collect(Collectors.joining("."));
+ return String.join(".", encodedSegments);
}
@Override
- public void decode(String encodedSection) throws DecodingException {
- String[] encodedSegments = encodedSection.split("\\.");
- String[] segmentBitStrings = new String[4];
- for (int i = 0; i < encodedSegments.length; i++) {
- /**
- * first char will contain 6 bits, we only need the first 3. In version 1 and 2 of the TC string
- * there is no segment type for the CORE string. Instead the first 6 bits are reserved for the
- * encoding version, but because we're only on a maximum of encoding version 2 the first 3 bits in
- * the core segment will evaluate to 0.
- */
- String segmentBitString = Base64UrlEncoder.decode(encodedSegments[i]);
- switch (segmentBitString.substring(0, 3)) {
- // unfortunately, the segment ordering doesn't match the segment ids
- case "000": {
- segmentBitStrings[0] = segmentBitString;
- break;
- }
- case "001": {
- segmentBitStrings[3] = segmentBitString;
- break;
- }
- case "010": {
- segmentBitStrings[2] = segmentBitString;
- break;
- }
- case "011": {
- segmentBitStrings[1] = segmentBitString;
- break;
- }
- default: {
- throw new DecodingException("Unable to decode segment '" + encodedSegments[i] + "'");
- }
- }
- }
- this.decodeSegmentsFromBitStrings(Arrays.asList(segmentBitStrings));
- }
-
- @Override
- public void setFieldValue(String fieldName, Object value) {
+ public void setFieldValue(String fieldName, Object value) throws InvalidFieldException {
super.setFieldValue(fieldName, value);
if (!fieldName.equals(TcfEuV2Field.CREATED) && !fieldName.equals(TcfEuV2Field.LAST_UPDATED)) {
@@ -219,144 +134,130 @@ public void setFieldValue(String fieldName, Object value) {
}
}
- @Override
- public int getId() {
- return TcfEuV2.ID;
- }
-
- @Override
- public String getName() {
- return TcfEuV2.NAME;
- }
-
- public Integer getVersion() {
- return (Integer) this.fields.get(TcfEuV2Field.VERSION).getValue();
- }
-
+
public ZonedDateTime getCreated() {
- return (ZonedDateTime) this.fields.get(TcfEuV2Field.CREATED).getValue();
+ return (ZonedDateTime) this.getFieldValue(TcfEuV2Field.CREATED);
}
public ZonedDateTime getLastUpdated() {
- return (ZonedDateTime) this.fields.get(TcfEuV2Field.LAST_UPDATED).getValue();
+ return (ZonedDateTime) this.getFieldValue(TcfEuV2Field.LAST_UPDATED);
}
public Integer getCmpId() {
- return (Integer) this.fields.get(TcfEuV2Field.CMP_ID).getValue();
+ return (Integer) this.getFieldValue(TcfEuV2Field.CMP_ID);
}
public Integer getCmpVersion() {
- return (Integer) this.fields.get(TcfEuV2Field.CMP_VERSION).getValue();
+ return (Integer) this.getFieldValue(TcfEuV2Field.CMP_VERSION);
}
public Integer getConsentScreen() {
- return (Integer) this.fields.get(TcfEuV2Field.CONSENT_SCREEN).getValue();
+ return (Integer) this.getFieldValue(TcfEuV2Field.CONSENT_SCREEN);
}
public String getConsentLanguage() {
- return (String) this.fields.get(TcfEuV2Field.CONSENT_LANGUAGE).getValue();
+ return (String) this.getFieldValue(TcfEuV2Field.CONSENT_LANGUAGE);
}
public Integer getVendorListVersion() {
- return (Integer) this.fields.get(TcfEuV2Field.VENDOR_LIST_VERSION).getValue();
+ return (Integer) this.getFieldValue(TcfEuV2Field.VENDOR_LIST_VERSION);
}
public Integer getPolicyVersion() {
- return (Integer) this.fields.get(TcfEuV2Field.POLICY_VERSION).getValue();
+ return (Integer) this.getFieldValue(TcfEuV2Field.POLICY_VERSION);
}
public Boolean getIsServiceSpecific() {
- return (Boolean) this.fields.get(TcfEuV2Field.IS_SERVICE_SPECIFIC).getValue();
+ return (Boolean) this.getFieldValue(TcfEuV2Field.IS_SERVICE_SPECIFIC);
}
public Boolean getUseNonStandardStacks() {
- return (Boolean) this.fields.get(TcfEuV2Field.USE_NON_STANDARD_STACKS).getValue();
+ return (Boolean) this.getFieldValue(TcfEuV2Field.USE_NON_STANDARD_STACKS);
}
@SuppressWarnings("unchecked")
- public List getSpecialFeatureOptins() {
- return (List) this.fields.get(TcfEuV2Field.SPECIAL_FEATURE_OPTINS).getValue();
+ public List getSpecialFeatureOptins() {
+ return (List) this.getFieldValue(TcfEuV2Field.SPECIAL_FEATURE_OPTINS);
}
@SuppressWarnings("unchecked")
- public List getPurposeConsents() {
- return (List) this.fields.get(TcfEuV2Field.PURPOSE_CONSENTS).getValue();
+ public List getPurposeConsents() {
+ return (List) this.getFieldValue(TcfEuV2Field.PURPOSE_CONSENTS);
}
@SuppressWarnings("unchecked")
- public List getPurposeLegitimateInterests() {
- return (List) this.fields.get(TcfEuV2Field.PURPOSE_LEGITIMATE_INTERESTS).getValue();
+ public List getPurposeLegitimateInterests() {
+ return (List) this.getFieldValue(TcfEuV2Field.PURPOSE_LEGITIMATE_INTERESTS);
}
public Boolean getPurposeOneTreatment() {
- return (Boolean) this.fields.get(TcfEuV2Field.PURPOSE_ONE_TREATMENT).getValue();
+ return (Boolean) this.getFieldValue(TcfEuV2Field.PURPOSE_ONE_TREATMENT);
}
public String getPublisherCountryCode() {
- return (String) this.fields.get(TcfEuV2Field.PUBLISHER_COUNTRY_CODE).getValue();
+ return (String) this.getFieldValue(TcfEuV2Field.PUBLISHER_COUNTRY_CODE);
}
@SuppressWarnings("unchecked")
public List getVendorConsents() {
- return (List) this.fields.get(TcfEuV2Field.VENDOR_CONSENTS).getValue();
+ return (List) this.getFieldValue(TcfEuV2Field.VENDOR_CONSENTS);
}
@SuppressWarnings("unchecked")
public List getVendorLegitimateInterests() {
- return (List) this.fields.get(TcfEuV2Field.VENDOR_LEGITIMATE_INTERESTS).getValue();
+ return (List) this.getFieldValue(TcfEuV2Field.VENDOR_LEGITIMATE_INTERESTS);
}
@SuppressWarnings("unchecked")
- public List getPublisherRestrictions() {
- return (List