11package io .frictionlessdata .tableschema .fk ;
22
33import com .fasterxml .jackson .annotation .JsonIgnore ;
4- import com .fasterxml .jackson .databind .JsonNode ;
5- import com .fasterxml .jackson .databind .node .ArrayNode ;
4+ import com .fasterxml .jackson .annotation .JsonProperty ;
65import io .frictionlessdata .tableschema .Table ;
76import io .frictionlessdata .tableschema .exception .ForeignKeyException ;
7+ import io .frictionlessdata .tableschema .exception .ValidationException ;
88import io .frictionlessdata .tableschema .util .JsonUtil ;
99
1010import java .util .*;
1515public class ForeignKey {
1616 private static final String JSON_KEY_FIELDS = "fields" ;
1717 private static final String JSON_KEY_REFERENCE = "reference" ;
18-
18+
19+ @ JsonProperty (JSON_KEY_FIELDS )
1920 private Object fields = null ;
21+
22+ @ JsonProperty (JSON_KEY_REFERENCE )
2023 private Reference reference = null ;
2124
22- private boolean strictValidation = false ;
23- private final ArrayList <Exception > errors = new ArrayList <>();
24-
25- public ForeignKey (){
26- }
25+ private boolean strictValidation = true ;
26+ private final ArrayList <ValidationException > errors = new ArrayList <>();
2727
28- public ForeignKey (boolean strict ){
29- this ();
30- this .strictValidation = strict ;
31- }
28+ public ForeignKey (){}
3229
3330 public ForeignKey (Object fields , Reference reference , boolean strict ) throws ForeignKeyException {
3431 this .fields = fields ;
3532 this .reference = reference ;
3633 this .strictValidation = strict ;
3734 this .validate ();
3835 }
39-
40- public ForeignKey (String json , boolean strict ) throws ForeignKeyException {
41- JsonNode fkJsonObject = JsonUtil .getInstance ().readValue (json );
42- this .strictValidation = strict ;
43-
44- if (fkJsonObject .has (JSON_KEY_FIELDS )){
45- if (fkJsonObject .get (JSON_KEY_FIELDS ).isArray ()) {
46- fields = fkJsonObject .get (JSON_KEY_FIELDS );
47- } else {
48- fields = fkJsonObject .get (JSON_KEY_FIELDS ).asText ();
49- }
50- }
51-
52- if (fkJsonObject .has (JSON_KEY_REFERENCE )){
53- JsonNode refJsonObject = fkJsonObject .get (JSON_KEY_REFERENCE );
54- reference = new Reference (refJsonObject .toString (), strict );
55- }
56-
57- validate ();
58- }
59-
36+
6037 public void setFields (Object fields ){
6138 this .fields = fields ;
6239 }
63-
40+
6441 public <Any > Any getFields (){
6542 return (Any )this .fields ;
6643 }
44+
45+ @ JsonIgnore
46+ public List <String > getFieldNames (){
47+ if (this .fields instanceof String ){
48+ return Collections .singletonList ((String )this .fields );
49+ } else if (this .fields instanceof Collection ){
50+ return new ArrayList <>( (Collection <String >)this .fields );
51+ } else {
52+ throw new IllegalArgumentException ("Invalid fields type in reference: " +this .fields .getClass ().getName ());
53+ }
54+ }
6755
6856 public void setReference (Reference reference ){
6957 this .reference = reference ;
@@ -75,38 +63,51 @@ public Reference getReference(){
7563
7664 public final void validate () throws ForeignKeyException {
7765 ForeignKeyException fke = null ;
78-
66+
7967 if (this .fields == null || this .reference == null ){
8068 fke = new ForeignKeyException ("A foreign key must have the fields and reference properties." );
8169
82- }else if (!(this .fields instanceof String ) && !(this .fields instanceof ArrayNode )){
70+ }else if (!(this .fields instanceof String ) && !(this .fields instanceof Collection )){
8371 fke = new ForeignKeyException ("The foreign key's fields property must be a string or an array." );
8472
85- }else if (this .fields instanceof ArrayNode && !(this .reference .getFields () instanceof ArrayNode )){
73+ }else if (this .fields instanceof Collection && !(this .reference .getFields () instanceof Collection )){
8674 fke = new ForeignKeyException ("The reference's fields property must be an array if the outer fields is an array." );
8775
8876 }else if (this .fields instanceof String && !(this .reference .getFields () instanceof String )){
8977 fke = new ForeignKeyException ("The reference's fields property must be a string if the outer fields is a string." );
9078
91- }else if (this .fields instanceof ArrayNode && this .reference .getFields () instanceof ArrayNode ){
92- ArrayNode fkFields = (ArrayNode )fields ;
93- ArrayNode refFields = reference .getFields ();
79+ }else if (this .fields instanceof Collection && this .reference .getFields () instanceof Collection ){
80+ Collection <?> fkFields = (Collection <?> )fields ;
81+ Collection <?> refFields = reference .getFields ();
9482
9583 if (fkFields .size () != refFields .size ()){
9684 fke = new ForeignKeyException ("The reference's fields property must be an array of the same length as that of the outer fields' array." );
9785 }
9886 }
9987
88+ if (null != reference ) {
89+ reference .validate ();
90+ }
91+
10092 if (fke != null ){
10193 if (this .strictValidation ){
10294 throw fke ;
10395 }else {
10496 errors .add (fke );
97+ if (null != reference ) {
98+ errors .addAll (reference .getErrors ());
99+ }
105100 }
106101 }
107102
108103 }
109104
105+ /**
106+ * validate the foreign key against the table. We only can validate self-referencing FKs, as we
107+ * do not have access to the tables in different resources of a datapackages in the tableschema library.
108+ * @param table the table to validate against
109+ * @throws ForeignKeyException if the foreign key is violated
110+ */
110111 public final void validate (Table table ) throws ForeignKeyException {
111112 validate ();
112113
@@ -117,10 +118,11 @@ public final void validate(Table table) throws ForeignKeyException{
117118 if (fields instanceof String ) {
118119 fieldNames .add ((String ) fields );
119120 foreignFieldNames .add (reference .getFields ());
120- } else if (fields instanceof ArrayNode ) {
121- for (int i = 0 ; i < ((ArrayNode ) fields ).size (); i ++) {
122- fieldNames .add (((ArrayNode ) fields ).get (i ).asText ());
123- foreignFieldNames .add (((ArrayNode ) reference .getFields ()).get (i ).asText ());
121+ } else if (fields instanceof Collection ) {
122+ List <String > lFields = getFieldNames ();
123+ for (int i = 0 ; i < lFields .size (); i ++) {
124+ fieldNames .add (lFields .get (i ));
125+ foreignFieldNames .add (reference .getFieldNames ().get (i ));
124126 }
125127 }
126128 Iterator <Object > iterator = table .iterator (true , false , false , false );
@@ -135,16 +137,19 @@ public final void validate(Table table) throws ForeignKeyException{
135137 }
136138 }
137139 }
138- }
140+ } else {
141+ throw new UnsupportedOperationException ("Foreign key references across package resources are not supported" );
142+ }
139143
140144 }
141145
142146 @ JsonIgnore
143147 public String getJson (){
144148 return JsonUtil .getInstance ().serialize (this );
145149 }
146-
147- public List <Exception > getErrors (){
150+
151+ @ JsonIgnore
152+ public ArrayList <ValidationException > getErrors (){
148153 return errors ;
149154 }
150155
0 commit comments