# HG changeset patch
# User Markus Mützel <markus.muetzel@gmx.de>
# Date 1480098345 -3600
#      Fri Nov 25 19:25:45 2016 +0100
# Node ID 199ae45acd9a9d7ee69298e16fc28f15d9da4e4b
# Parent  c88f8829678963c40d11a74368e2f49ff247dc6a
Add input checking for double_property and handle_property (task #11311)

* graphics.in.h (class double_property): Add protected members minval
and maxval to store min and max allowed value for double properties.
* graphics.in.h (double_property::add_constraint (const std::string& type, double val, bool inclusive)):
Add method to set the range of allowed values.
* graphics.in.h (class handle_property): New protected member
type_constraints to store allowed object types.
* graphics.in.h (handle_property::add_constraint (const std::string& type)):
Add allowed object types.
* graphics.in.h (init functions): Add range and type constraints.

* graphics.in.h (double_property::do_set): Check allowed values.
* graphics.cc (handle_property::do_set): Check allowed objects type.
* graphics.cc (init): Add range constraints for double properties.
* graphics.cc: Add BIST tests.

diff -r c88f88296789 -r 199ae45acd9a libinterp/corefcn/graphics.cc
--- a/libinterp/corefcn/graphics.cc	Thu Nov 24 09:00:20 2016 -0800
+++ b/libinterp/corefcn/graphics.cc	Fri Nov 25 19:25:45 2016 +0100
@@ -1582,9 +1582,32 @@
 
   graphics_handle gh = gh_manager::lookup (dv);
 
-  if (! (octave::math::isnan (gh.value ()) || gh.ok ()))
-    error ("set: invalid graphics handle (= %g) for property \"%s\"",
-           dv, get_name ().c_str ());
+  // Check the object type if necessary
+  bool type_ok = true;
+  if (gh.ok () && ! type_constraints.empty ())
+    {
+      type_ok = false;
+      graphics_object obj = gh_manager::get_object (gh);
+
+      for (std::set<std::string>::const_iterator
+           p = type_constraints.begin ();
+           p != type_constraints.end (); p++)
+        if (obj.isa (*p))
+          {
+            type_ok = true;
+            break;
+          }
+    }
+
+  if (! octave::math::isnan (gh.value ()) && ! (gh.ok () && type_ok))
+    {
+      if (type_ok)
+        error ("set: invalid graphics handle (= %g) for property \"%s\"",
+               dv, get_name ().c_str ());
+      else
+        error ("set: invalid graphics object type for property \"%s\"",
+               get_name ().c_str ());
+    }
 
   if (current_val != gh)
     {
@@ -1595,6 +1618,24 @@
   return false;
 }
 
+/*
+## Test validation of uicontextmenu property 
+%!test
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   hax = axes ("parent", hf);
+%!   hpa = patch ("parent", hax);
+%!   try 
+%!     set (hax, "uicontextmenu", hpa);
+%!   catch
+%!     err = lasterr ();
+%!   end_try_catch
+%!   assert (err, "set: invalid graphics object type for property \"uicontextmenu\"");
+%! unwind_protect_cleanup
+%!   delete (hf)
+%! end_unwind_protect   
+*/
+
 Matrix
 children_property::do_get_children (bool return_hidden) const
 {
@@ -4508,6 +4549,17 @@
   cameraupvector = upv;
   cameraupvector.add_constraint (dim_vector (1, 3));
   currentpoint.add_constraint (dim_vector (2, 3));
+
+  // Range constraints for double properties
+  fontsize.add_constraint ("min", 0.0, false);
+  gridalpha.add_constraint ("min", 0.0, true);
+  gridalpha.add_constraint ("max", 1.0, true);
+  labelfontsizemultiplier.add_constraint ("min", 0.0, false);
+  linewidth.add_constraint ("min", 0.0, false);
+  minorgridalpha.add_constraint ("min", 0.0, true);
+  minorgridalpha.add_constraint ("max", 1.0, true);
+  titlefontsizemultiplier.add_constraint ("min", 0.0, false);
+
   // No constraints for hidden transform properties
   update_font ();
 
@@ -4568,6 +4620,29 @@
   looseinset = tlooseinset;
 }
 
+/*
+## Test validation of axes double properties range 
+%!test
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   hax =  axes ("parent", hf);
+%!   try 
+%!     set (hax, "linewidth", -1);
+%!   catch
+%!     err = lasterr ();
+%!   end_try_catch
+%!   assert (err, "set: \"linewidth\" must be greater than 0")
+%!   try 
+%!     set (hax, "minorgridalpha", 1.5);
+%!   catch
+%!     err = lasterr ();
+%!   end_try_catch
+%!   assert (err, "set: \"minorgridalpha\" must be less than or equal to 1")
+%! unwind_protect_cleanup
+%!   delete (hf)
+%! end_unwind_protect   
+*/
+
 Matrix
 axes::properties::calc_tightbox (const Matrix& init_pos)
 {
diff -r c88f88296789 -r 199ae45acd9a libinterp/corefcn/graphics.in.h
--- a/libinterp/corefcn/graphics.in.h	Thu Nov 24 09:00:20 2016 -0800
+++ b/libinterp/corefcn/graphics.in.h	Fri Nov 25 19:25:45 2016 +0100
@@ -1155,10 +1155,14 @@
   double_property (const std::string& nm, const graphics_handle& h,
                    double d = 0)
     : base_property (nm, h),
-      current_val (d) { }
+      current_val (d),
+      minval (std::pair<double, bool> (octave_NaN, true)),
+      maxval (std::pair<double, bool> (octave_NaN, true)) { }
 
   double_property (const double_property& p)
-    : base_property (p), current_val (p.current_val) { }
+    : base_property (p), current_val (p.current_val),
+      minval (std::pair<double, bool> (octave_NaN, true)),
+      maxval (std::pair<double, bool> (octave_NaN, true)) { }
 
   octave_value get (void) const { return octave_value (current_val); }
 
@@ -1172,6 +1176,14 @@
 
   base_property* clone (void) const { return new double_property (*this); }
 
+  void add_constraint (const std::string& type, double val, bool inclusive)
+  { 
+    if (type == "min")
+      minval = std::pair<double, bool> (val, inclusive);
+    else if (type == "max")
+      maxval = std::pair<double, bool> (val, inclusive);
+  }
+
 protected:
   bool do_set (const octave_value& v)
   {
@@ -1181,6 +1193,39 @@
 
     double new_val = v.double_value ();
 
+    // Check min and max
+    if (! octave::math::isnan (minval.first))
+      {
+        if (minval.second && minval.first > new_val)
+          {
+            error ("set: \"%s\" must be greater than or equal to %g",
+                   get_name ().c_str (), minval.first);
+            return false;
+          }
+        else if (! minval.second && minval.first >= new_val)
+          {
+            error ("set: \"%s\" must be greater than %g",
+                   get_name ().c_str (), minval.first);
+            return false;
+          }
+      }
+
+    if (! octave::math::isnan (maxval.first))
+      {
+        if (maxval.second && maxval.first < new_val)
+          {
+            error ("set: \"%s\" must be less than or equal to %g",
+                   get_name ().c_str (), maxval.first);
+            return false;
+          }
+        else if (! maxval.second && maxval.first <= new_val)
+          {
+            error ("set: \"%s\" must be less than %g",
+                   get_name ().c_str (), maxval.first);
+            return false;
+          }
+      }
+
     if (new_val != current_val)
       {
         current_val = new_val;
@@ -1192,6 +1237,7 @@
 
 private:
   double current_val;
+  std::pair<double, bool> minval, maxval;
 };
 
 // ---------------------------------------------------------------------
@@ -1537,8 +1583,12 @@
 
   base_property* clone (void) const { return new handle_property (*this); }
 
+  void add_constraint (const std::string& type)
+  { type_constraints.insert (type); }
+
 protected:
   OCTINTERP_API bool do_set (const octave_value& v);
+  std::set<std::string> type_constraints;
 
 private:
   graphics_handle current_val;
@@ -2639,7 +2689,10 @@
   void insert_static_property (const std::string& name, base_property& p)
   { insert_property (name, property (&p, true)); }
 
-  virtual void init (void) { }
+  virtual void init (void) 
+  { 
+    uicontextmenu.add_constraint ("uicontextmenu");
+  }
 };
 
 class OCTINTERP_API base_graphics_object
@@ -4409,6 +4462,13 @@
       bool_property zliminclude hl , "off"
     END_PROPERTIES
 
+  protected:
+    void init (void)
+      {
+        linewidth.add_constraint ("min", 0, false);
+        markersize.add_constraint ("min", 0, false);
+      }
+
   private:
     Matrix compute_xlim (void) const;
     Matrix compute_ylim (void) const;
@@ -4536,6 +4596,9 @@
     void init (void)
     {
       position.add_constraint (dim_vector (1, 3));
+      fontsize.add_constraint ("min", 0.0, false);
+      linewidth.add_constraint ("min", 0.0, false);
+      margin.add_constraint ("min", 0.0, false);
       cached_units = get_units ();
       update_font ();
     }
@@ -4982,6 +5045,18 @@
       facenormals.add_constraint (dim_vector (0, 0));
       vertexnormals.add_constraint (dim_vector (-1, 3));
       vertexnormals.add_constraint (dim_vector (0, 0));
+
+      ambientstrength.add_constraint ("min", 0.0, true);
+      ambientstrength.add_constraint ("max", 1.0, true);
+      diffusestrength.add_constraint ("min", 0.0, true);
+      diffusestrength.add_constraint ("max", 1.0, true);
+      linewidth.add_constraint ("min", 0.0, false);
+      markersize.add_constraint ("min", 0.0, false);
+      specularcolorreflectance.add_constraint ("min", 0.0, true);
+      specularcolorreflectance.add_constraint ("max", 1.0, true);
+      specularexponent.add_constraint ("min", 0.0, false);
+      specularstrength.add_constraint ("min", 0.0, true);
+      specularstrength.add_constraint ("max", 1.0, true);
     }
 
   private:
@@ -5184,6 +5259,18 @@
       facenormals.add_constraint (dim_vector (0, 0));
       vertexnormals.add_constraint (dim_vector (-1, -1, 3));
       vertexnormals.add_constraint (dim_vector (0, 0));
+
+      ambientstrength.add_constraint ("min", 0.0, true);
+      ambientstrength.add_constraint ("max", 1.0, true);
+      diffusestrength.add_constraint ("min", 0.0, true);
+      diffusestrength.add_constraint ("max", 1.0, true);
+      linewidth.add_constraint ("min", 0.0, false);
+      markersize.add_constraint ("min", 0.0, false);
+      specularcolorreflectance.add_constraint ("min", 0.0, true);
+      specularcolorreflectance.add_constraint ("max", 1.0, true);
+      specularexponent.add_constraint ("min", 0.0, false);
+      specularstrength.add_constraint ("min", 0.0, true);
+      specularstrength.add_constraint ("max", 1.0, true);
     }
 
   private:
@@ -5390,7 +5477,9 @@
 
   protected:
     void init (void)
-    { }
+    {
+      position.add_constraint ("min", 0, false);
+    }
   };
 
 private:
@@ -5542,6 +5631,7 @@
       cdata.add_constraint (dim_vector (-1, -1, 3));
       position.add_constraint (dim_vector (1, 4));
       sliderstep.add_constraint (dim_vector (1, 2));
+      fontsize.add_constraint ("min", 0.0, false);
       cached_units = get_units ();
     }
 
@@ -5628,6 +5718,8 @@
     void init (void)
     {
       position.add_constraint (dim_vector (1, 4));
+      borderwidth.add_constraint ("min", 0.0, true);
+      fontsize.add_constraint ("min", 0.0, false);
     }
 
     // void update_text_extent (void);
@@ -5708,6 +5800,8 @@
   protected:
     void init (void)
     {
+      borderwidth.add_constraint ("min", 0.0, true);
+      fontsize.add_constraint ("min", 0.0, false);
       position.add_constraint (dim_vector (1, 4));
     }
 
