Introduce serialization and deserialization of constraint queries

Change-Id: Ib6515bef7c1ea9f909a16463dd73ec5342d2a3c9
diff --git a/lib/Krawfish/Koral/Query/Boundary.pm b/lib/Krawfish/Koral/Query/Boundary.pm
index 2f12b2a..d29d331 100644
--- a/lib/Krawfish/Koral/Query/Boundary.pm
+++ b/lib/Krawfish/Koral/Query/Boundary.pm
@@ -4,7 +4,7 @@
 use warnings;
 
 # Serialization helper
-sub boundary {
+sub to_koral_boundary {
   my $self = shift;
   my %hash = (
     '@type' => 'koral:boundary'
@@ -15,6 +15,22 @@
 };
 
 
+# Deserialization helper
+sub from_koral_boundary {
+  my ($class, $kq) = @_;
+
+  my ($min, $max);
+  if ($kq->{min}) {
+    $min = $kq->{min};
+  };
+  if ($kq->{max}) {
+    $max = $kq->{max};
+  };
+
+  return ($min, $max);
+};
+
+
 sub min {
   if (defined $_[1]) {
     $_[0]->{min} = $_[1];
diff --git a/lib/Krawfish/Koral/Query/Builder.pm b/lib/Krawfish/Koral/Query/Builder.pm
index f213993..6a20daf 100644
--- a/lib/Krawfish/Koral/Query/Builder.pm
+++ b/lib/Krawfish/Koral/Query/Builder.pm
@@ -21,7 +21,7 @@
 
 # TODO: Not all constraints need to be wrapped
 use Krawfish::Koral::Query::Constraint::Position;
-use Krawfish::Koral::Query::Constraint::ClassDistance;
+use Krawfish::Koral::Query::Constraint::ClassBetween;
 use Krawfish::Koral::Query::Constraint::NotBetween;
 use Krawfish::Koral::Query::Constraint::InBetween;
 
@@ -176,9 +176,9 @@
   Krawfish::Koral::Query::Constraint::Position->new(@_);
 };
 
-sub c_class_distance {
+sub c_class_between {
   shift;
-  Krawfish::Koral::Query::Constraint::ClassDistance->new(@_);
+  Krawfish::Koral::Query::Constraint::ClassBetween->new(@_);
 };
 
 sub c_not_between {
diff --git a/lib/Krawfish/Koral/Query/Constraint/Base.pm b/lib/Krawfish/Koral/Query/Constraint/Base.pm
new file mode 100644
index 0000000..1bddd9c
--- /dev/null
+++ b/lib/Krawfish/Koral/Query/Constraint/Base.pm
@@ -0,0 +1,20 @@
+package Krawfish::Koral::Query::Constraint::Base;
+use Role::Tiny;
+use strict;
+use warnings;
+
+requires qw/type to_string optimize min_span max_span/;
+
+# Normalize the constraint (do nothing)
+sub normalize {
+  $_[0];
+};
+
+
+# Identify the constraint (do nothing)
+sub identify {
+  $_[0];
+};
+
+
+1;
diff --git a/lib/Krawfish/Koral/Query/Constraint/ClassBetween.pm b/lib/Krawfish/Koral/Query/Constraint/ClassBetween.pm
new file mode 100644
index 0000000..e2b7ffe
--- /dev/null
+++ b/lib/Krawfish/Koral/Query/Constraint/ClassBetween.pm
@@ -0,0 +1,67 @@
+package Krawfish::Koral::Query::Constraint::ClassBetween;
+use Krawfish::Query::Constraint::ClassBetween;
+use Role::Tiny::With;
+use strict;
+use warnings;
+
+with 'Krawfish::Koral::Query::Constraint::Base';
+
+# This will add a class to the distance between both queries
+
+sub new {
+  my $class = shift;
+  my $nr = shift // 1;
+  bless \$nr, $class;
+};
+
+sub type {
+  'constr_class';
+};
+
+
+# stringification
+sub to_string {
+  my $self = shift;
+  return 'class=' . $$self;
+};
+
+
+# Optimize the constraint
+sub optimize {
+  my $self = shift;
+  Krawfish::Query::Constraint::ClassBetween->new($$self);
+};
+
+
+# Deserialize
+sub from_koral {
+  my ($class, $kq) = @_;
+  my $nr = $kq->{classOut};
+  return $class->new($nr);
+};
+
+
+# Serialize
+sub to_koral_fragment {
+  my $self = shift;
+  return {
+    '@type' => 'constraint:classBetween',
+    'classOut' => $$self
+  };
+};
+
+
+# The minimum number of tokens for the constraint
+# Is actual at least one token - but could be optional
+sub min_span {
+  0;
+};
+
+
+# Maximum number of tokens for the constraint
+sub max_span {
+  -1;
+};
+
+
+1;
diff --git a/lib/Krawfish/Koral/Query/Constraint/ClassDistance.pm b/lib/Krawfish/Koral/Query/Constraint/ClassDistance.pm
deleted file mode 100644
index d8a0d9e..0000000
--- a/lib/Krawfish/Koral/Query/Constraint/ClassDistance.pm
+++ /dev/null
@@ -1,60 +0,0 @@
-package Krawfish::Koral::Query::Constraint::ClassDistance;
-use Krawfish::Query::Constraint::ClassDistance;
-use strict;
-use warnings;
-
-# This will add a class to the distance between both queries
-
-# TODO: Rename to ClassBetween
-
-sub new {
-  my $class = shift;
-  my $nr = shift // 1;
-  bless \$nr, $class;
-};
-
-sub type {
-  'constr_class';
-};
-
-
-# stringification
-sub to_string {
-  my $self = shift;
-  return 'class=' . $$self;
-};
-
-
-# Normalize the constraint (do nothing)
-sub normalize {
-  $_[0];
-};
-
-
-# Identify the constraint (do nothing)
-sub identify {
-  $_[0];
-};
-
-
-# Optimize the constraint
-sub optimize {
-  my $self = shift;
-  Krawfish::Query::Constraint::ClassDistance->new($$self);
-};
-
-
-# The minimum number of tokens for the constraint
-# Is actual at least one token - but could be optional
-sub min_span {
-  0;
-};
-
-
-# Maximum number of tokens for the constraint
-sub max_span {
-  -1;
-};
-
-
-1;
diff --git a/lib/Krawfish/Koral/Query/Constraint/InBetween.pm b/lib/Krawfish/Koral/Query/Constraint/InBetween.pm
index d30c307..4355dba 100644
--- a/lib/Krawfish/Koral/Query/Constraint/InBetween.pm
+++ b/lib/Krawfish/Koral/Query/Constraint/InBetween.pm
@@ -1,8 +1,12 @@
 package Krawfish::Koral::Query::Constraint::InBetween;
 use Krawfish::Query::Constraint::InBetween;
+use Role::Tiny::With;
 use strict;
 use warnings;
 
+with 'Krawfish::Koral::Query::Constraint::Base';
+with 'Krawfish::Koral::Query::Boundary';
+
 # TODO:
 #   Support foundry for tokenization
 #   and gaps parameter.
@@ -25,29 +29,12 @@
 # Stringify
 sub to_string {
   my $self = shift;
-  return 'between=' . (defined $self->{min} ? $self->{min} : 0) . '-' . (defined $self->{max} ? $self->{max} : 'INF');
+  return 'between=' . (defined $self->{min} ? $self->{min} : 0) .
+    '-' .
+    (defined $self->{max} ? $self->{max} : 'INF');
 };
 
 
-sub min {
-  my $self = shift;
-  if ($_[0]) {
-    $self->{min} = shift;
-    return $self;
-  };
-  $self->{min};
-};
-
-
-sub max {
-  my $self = shift;
-  if ($_[0]) {
-    $self->{max} = shift;
-    return $self;
-  };
-  $self->{max};
-};
-
 
 # Normalize constraint
 sub normalize {
@@ -67,11 +54,6 @@
 };
 
 
-sub identify {
-  $_[0];
-};
-
-
 # Optimize constraint
 sub optimize {
   my $self = shift;
@@ -110,4 +92,27 @@
 };
 
 
+# Deserialize
+sub from_koral {
+  my ($class, $kq) = @_;
+
+  return $class->new(
+    $class->from_koral_boundary(
+      $kq->{boundary}
+    )
+  );
+};
+
+
+# serialize
+sub to_koral_fragment {
+  my $self = shift;
+
+  return {
+    '@type' => 'constraint:inBetween',
+    boundary => $self->to_koral_boundary
+  };
+};
+
+
 1;
diff --git a/lib/Krawfish/Koral/Query/Constraint/NotBetween.pm b/lib/Krawfish/Koral/Query/Constraint/NotBetween.pm
index 577ac42..21aebf7 100644
--- a/lib/Krawfish/Koral/Query/Constraint/NotBetween.pm
+++ b/lib/Krawfish/Koral/Query/Constraint/NotBetween.pm
@@ -1,11 +1,15 @@
 package Krawfish::Koral::Query::Constraint::NotBetween;
+use Role::Tiny::With;
 use Krawfish::Query::Constraint::NotBetween;
 use Krawfish::Koral::Query::Constraint::InBetween;
-use Krawfish::Koral::Query::Constraint::ClassDistance;
+use Krawfish::Koral::Query::Constraint::ClassBetween;
 use Krawfish::Koral::Query::Constraint::Position;
+use Krawfish::Koral::Query::Importer;
 use strict;
 use warnings;
 
+with 'Krawfish::Koral::Query::Constraint::Base';
+
 # Check that a query between two operands is does nmot occur.
 # In case this operand never occurs, it will at least set a relevant length.
 
@@ -44,7 +48,7 @@
 
   # Wrap out the classes
   while ($query->type eq 'class') {
-    push @constraints, Krawfish::Koral::Query::Constraint::ClassDistance->new($query->number);
+    push @constraints, Krawfish::Koral::Query::Constraint::ClassBetween->new($query->number);
     $query = $query->operand;
   };
 
@@ -129,4 +133,27 @@
 };
 
 
+# Deserialize
+sub from_koral {
+  my ($class, $kq) = @_;
+
+  my $importer = Krawfish::Koral::Query::Importer->new;
+
+  my $wrap = $kq->{wrap};
+  return $class->new(
+    $importer->from_koral($wrap)
+  );
+};
+
+
+# Serialize
+sub to_koral_fragment {
+  my $self = shift;
+  return {
+    '@type' => 'constraint:notBetween',
+    'wrap' => $self->{query}->to_koral_fragment
+  };
+};
+
+
 1;
diff --git a/lib/Krawfish/Koral/Query/Constraint/Position.pm b/lib/Krawfish/Koral/Query/Constraint/Position.pm
index d321515..bce7bc8 100644
--- a/lib/Krawfish/Koral/Query/Constraint/Position.pm
+++ b/lib/Krawfish/Koral/Query/Constraint/Position.pm
@@ -1,4 +1,5 @@
 package Krawfish::Koral::Query::Constraint::Position;
+use Role::Tiny::With;
 use parent 'Exporter';
 use Krawfish::Query::Constraint::Position;
 use feature 'state';
@@ -12,6 +13,8 @@
                  to_list
                  MATCHES/;
 
+with 'Krawfish::Koral::Query::Constraint::Base';
+
 # TODO:
 #   Add error etc. and base this on Krawfish::Query::Constraint::Base.
 
@@ -191,22 +194,21 @@
 };
 
 
-sub identify {
-  $_[0];
-};
-
+# Optimize constraint
 sub optimize {
   my $self = shift;
   Krawfish::Query::Constraint::Position->new($$self);
 };
 
 
+# Deserialize
 sub from_koral {
   my ($class, $kq) = @_;
   return $class->new(@{$kq->{frames}});
 };
 
 
+# serialize
 sub to_koral_fragment {
   my $self = shift;
   return {
@@ -217,4 +219,5 @@
   };
 };
 
+
 1;
diff --git a/lib/Krawfish/Koral/Query/Importer.pm b/lib/Krawfish/Koral/Query/Importer.pm
index caae132..78b8bfc 100644
--- a/lib/Krawfish/Koral/Query/Importer.pm
+++ b/lib/Krawfish/Koral/Query/Importer.pm
@@ -15,6 +15,9 @@
 use Krawfish::Koral::Query::Or;
 
 use Krawfish::Koral::Query::Constraint::Position;
+use Krawfish::Koral::Query::Constraint::ClassBetween;
+use Krawfish::Koral::Query::Constraint::NotBetween;
+use Krawfish::Koral::Query::Constraint::InBetween;
 
 # TODO:
 #   Merge with Builder!
@@ -87,9 +90,19 @@
   shift;
   my $kq = shift;
   if ($kq->{'@type'} eq 'constraint:position') {
-    return Krawfish::Koral::Query::Constraint::Position->new(
-      @{$kq->{frames}}
-    );
+    return Krawfish::Koral::Query::Constraint::Position->from_koral($kq);
+  }
+
+  elsif ($kq->{'@type'} eq 'constraint:classBetween') {
+    return Krawfish::Koral::Query::Constraint::ClassBetween->from_koral($kq);
+  }
+
+  elsif ($kq->{'@type'} eq 'constraint:notBetween') {
+    return Krawfish::Koral::Query::Constraint::NotBetween->from_koral($kq);
+  }
+
+  elsif ($kq->{'@type'} eq 'constraint:inBetween') {
+    return Krawfish::Koral::Query::Constraint::InBetween->from_koral($kq);
   };
 
   warn 'Type ' . $kq->{'@type'} . ' unknown';
diff --git a/lib/Krawfish/Koral/Query/Length.pm b/lib/Krawfish/Koral/Query/Length.pm
index 3326a9d..a0514d9 100644
--- a/lib/Krawfish/Koral/Query/Length.pm
+++ b/lib/Krawfish/Koral/Query/Length.pm
@@ -217,7 +217,7 @@
   return {
     '@type' => 'koral:group',
     operation => 'operation:length',
-    boundary => $self->boundary,
+    boundary => $self->to_koral_boundary,
     # token    => $self->token_base,
     operands => [
       $self->operand->to_koral_fragment
@@ -233,11 +233,9 @@
   my $importer = $class->importer;
 
   my @param = ();
-  my $boundary = $kq->{boundary};
-  if ($boundary) {
-    push @param, $boundary->{min} if $boundary->{min};
-    push @param, $boundary->{max} if $boundary->{max};
-  };
+  my ($min, $max) = $class->from_koral_boundary($kq->{boundary});
+  push @param, $min if defined $min;
+  push @param, $max if defined $max;
 
   # TODO:
   #   Not yet implemented
diff --git a/lib/Krawfish/Koral/Query/Repetition.pm b/lib/Krawfish/Koral/Query/Repetition.pm
index 2c09a36..428683c 100644
--- a/lib/Krawfish/Koral/Query/Repetition.pm
+++ b/lib/Krawfish/Koral/Query/Repetition.pm
@@ -75,7 +75,7 @@
   return {
     '@type' => 'koral:group',
     'operation' => 'operation:repetition',
-    'boundary' => $self->boundary,
+    'boundary' => $self->to_koral_boundary,
     'operands' => [
       $self->operand->to_koral_fragment
     ]
@@ -84,15 +84,8 @@
 
 sub from_koral {
   my ($class, $kq) = @_;
-  my $boundary = $kq->{boundary};
 
-  my ($min, $max);
-  if ($boundary->{min}) {
-    $min = $boundary->{min};
-  };
-  if ($boundary->{max}) {
-    $max = $boundary->{max};
-  };
+  my ($min, $max) = $class->from_koral_boundary($kq->{boundary});
 
   my $importer = $class->importer;
 
diff --git a/lib/Krawfish/Koral/Util/Sequential.pm b/lib/Krawfish/Koral/Util/Sequential.pm
index d1415e6..2162cfd 100644
--- a/lib/Krawfish/Koral/Util/Sequential.pm
+++ b/lib/Krawfish/Koral/Util/Sequential.pm
@@ -6,6 +6,7 @@
 use Krawfish::Query::Nowhere;
 use Krawfish::Query::Constraint::Position;
 use Krawfish::Query::Constraint::InBetween;
+use Krawfish::Query::Constraint::ClassBetween;
 use Krawfish::Query::Constraint;
 use List::MoreUtils qw!uniq!;
 
@@ -1160,7 +1161,7 @@
   if ($constraint->{classes}) {
     foreach (@{$constraint->{classes}}) {
       push @constraints,
-        Krawfish::Query::Constraint::ClassDistance->new($_);
+        Krawfish::Query::Constraint::ClassBetween->new($_);
     };
   };
 
diff --git a/lib/Krawfish/Query/Constraint/ClassDistance.pm b/lib/Krawfish/Query/Constraint/ClassBetween.pm
similarity index 93%
rename from lib/Krawfish/Query/Constraint/ClassDistance.pm
rename to lib/Krawfish/Query/Constraint/ClassBetween.pm
index 4fddab3..f2f17fb 100644
--- a/lib/Krawfish/Query/Constraint/ClassDistance.pm
+++ b/lib/Krawfish/Query/Constraint/ClassBetween.pm
@@ -1,4 +1,4 @@
-package Krawfish::Query::Constraint::ClassDistance;
+package Krawfish::Query::Constraint::ClassBetween;
 use Role::Tiny::With;
 use strict;
 use warnings;