Implemented feature request: a method to clear properties on shapes, instances, cells and layout

This commit is contained in:
Matthias Koefferlein 2025-07-26 15:26:47 +02:00
parent d6193c2862
commit 2369c69f69
14 changed files with 337 additions and 43 deletions

View File

@ -1611,10 +1611,8 @@ Instances::replace_prop_id (const instance_type &ref, db::properties_id_type pro
throw tl::Exception (tl::to_string (tr ("Trying to replace an object in a list that it does not belong to")));
}
if (! ref.is_null ()) {
if (ref.prop_id () != prop_id) {
invalidate_prop_ids ();
}
if (! ref.is_null () && (!ref.has_prop_id () || ref.prop_id () != prop_id)) {
invalidate_prop_ids ();
cell_inst_wp_array_type new_inst (ref.cell_inst (), prop_id);
return replace (ref, new_inst);
} else {
@ -1622,7 +1620,29 @@ Instances::replace_prop_id (const instance_type &ref, db::properties_id_type pro
}
}
void
Instances::instance_type
Instances::clear_properties (const instance_type &ref)
{
if (ref.instances () != this) {
throw tl::Exception (tl::to_string (tr ("Trying to replace an object in a list that it does not belong to")));
}
if (! ref.is_null () && ref.has_prop_id ()) {
invalidate_prop_ids ();
cell_inst_array_type new_inst (ref.cell_inst ());
erase (ref);
return insert (new_inst);
} else {
return ref;
}
}
void
Instances::do_clear_insts ()
{
if (m_generic.any) {

View File

@ -1497,6 +1497,13 @@ public:
*/
instance_type replace_prop_id (const instance_type &ref, db::properties_id_type prop_id);
/**
* @brief Clears the properties
*
* @return The reference to the new instance
*/
instance_type clear_properties (const instance_type &ref);
/**
* @brief Replace the instance pointed to by the iterator with the given instance
*

View File

@ -79,6 +79,17 @@ db::properties_id_type properties_id (const PropertiesSet &ps)
return PropertiesRepository::instance ().properties_id (ps);
}
db::properties_id_type properties_id (const std::map<tl::Variant, tl::Variant> &dict)
{
db::PropertiesSet props;
for (std::map<tl::Variant, tl::Variant>::const_iterator v = dict.begin (); v != dict.end (); ++v) {
props.insert (v->first, v->second);
}
return db::properties_id (props);
}
size_t hash_for_properties_id (properties_id_type id)
{
return id == 0 ? 0 : db::properties (id).hash ();

View File

@ -310,6 +310,11 @@ DB_PUBLIC const PropertiesSet &properties (db::properties_id_type id);
*/
DB_PUBLIC db::properties_id_type properties_id (const PropertiesSet &ps);
/**
* @brief Gets the properties ID from a raw properties set
*/
DB_PUBLIC db::properties_id_type properties_id (const std::map<tl::Variant, tl::Variant> &dict);
/**
* @brief The properties repository
*

View File

@ -1098,18 +1098,6 @@ public:
m_type = UserObject;
}
/**
* @brief Determine, if the shape is of "object_with_properties" type.
*
* Usually, the properties id (see prop_id()) should be used.
*
* @return true, if the shape is of "object_with_properties<Sh> type".
*/
bool with_props () const
{
return m_with_props;
}
/**
* @brief Get the properties Id associated with the shape
*/

View File

@ -738,16 +738,87 @@ Shapes::find (const Shapes::shape_type &shape) const
}
Shapes::shape_type
Shapes::replace_prop_id (const Shapes::shape_type &ref, db::properties_id_type prop_id)
Shapes::clear_properties (const Shapes::shape_type &ref)
{
tl_assert (! ref.is_array_member ());
if (ref.has_prop_id ()) {
if (ref.prop_id () != prop_id) {
invalidate_prop_ids ();
if (! is_editable ()) {
throw tl::Exception (tl::to_string (tr ("Function 'clear_properties' is permitted only in editable mode when going to property-less shapes from some with properties")));
}
switch (ref.m_type) {
case shape_type::Null:
return ref;
case shape_type::Polygon:
return clear_properties_iter (shape_type::polygon_type::tag (), ref.basic_iter (object_with_properties<shape_type::polygon_type>::tag ()));
case shape_type::PolygonRef:
return clear_properties_iter (shape_type::polygon_ref_type::tag (), ref.basic_iter (object_with_properties<shape_type::polygon_ref_type>::tag ()));
case shape_type::PolygonPtrArray:
return clear_properties_iter (shape_type::polygon_ptr_array_type::tag (), ref.basic_iter (object_with_properties<shape_type::polygon_ptr_array_type>::tag ()));
case shape_type::SimplePolygon:
return clear_properties_iter (shape_type::simple_polygon_type::tag (), ref.basic_iter (object_with_properties<shape_type::simple_polygon_type>::tag ()));
case shape_type::SimplePolygonRef:
return clear_properties_iter (shape_type::simple_polygon_ref_type::tag (), ref.basic_iter (object_with_properties<shape_type::simple_polygon_ref_type>::tag ()));
case shape_type::SimplePolygonPtrArray:
// HINT: since we are in editing mode, this type should not appear ..
return clear_properties_iter (shape_type::simple_polygon_ptr_array_type::tag (), ref.basic_iter (object_with_properties<shape_type::simple_polygon_ptr_array_type>::tag ()));
case shape_type::Edge:
return clear_properties_iter (shape_type::edge_type::tag (), ref.basic_iter (object_with_properties<shape_type::edge_type>::tag ()));
case shape_type::Point:
return clear_properties_iter (shape_type::point_type::tag (), ref.basic_iter (object_with_properties<shape_type::point_type>::tag ()));
case shape_type::EdgePair:
return clear_properties_iter (shape_type::edge_pair_type::tag (), ref.basic_iter (object_with_properties<shape_type::edge_pair_type>::tag ()));
case shape_type::Path:
return clear_properties_iter (shape_type::path_type::tag (), ref.basic_iter (object_with_properties<shape_type::path_type>::tag ()));
case shape_type::PathRef:
return clear_properties_iter (shape_type::path_ref_type::tag (), ref.basic_iter (object_with_properties<shape_type::path_ref_type>::tag ()));
case shape_type::PathPtrArray:
// HINT: since we are in editing mode, this type should not appear ..
return clear_properties_iter (shape_type::path_ptr_array_type::tag (), ref.basic_iter (object_with_properties<shape_type::path_ptr_array_type>::tag ()));
case shape_type::Box:
return clear_properties_iter (shape_type::box_type::tag (), ref.basic_iter (object_with_properties<shape_type::box_type>::tag ()));
case shape_type::BoxArray:
// HINT: since we are in editing mode, this type should not appear ..
return clear_properties_iter (shape_type::box_array_type::tag (), ref.basic_iter (object_with_properties<shape_type::box_array_type>::tag ()));
case shape_type::ShortBox:
return clear_properties_iter (shape_type::short_box_type::tag (), ref.basic_iter (object_with_properties<shape_type::short_box_type>::tag ()));
case shape_type::ShortBoxArray:
// HINT: since we are in editing mode, this type should not appear ..
return clear_properties_iter (shape_type::short_box_array_type::tag (), ref.basic_iter (object_with_properties<shape_type::short_box_array_type>::tag ()));
case shape_type::Text:
return clear_properties_iter (shape_type::text_type::tag (), ref.basic_iter (object_with_properties<shape_type::text_type>::tag ()));
case shape_type::TextRef:
return clear_properties_iter (shape_type::text_ref_type::tag (), ref.basic_iter (object_with_properties<shape_type::text_ref_type>::tag ()));
case shape_type::TextPtrArray:
// HINT: since we are in editing mode, this type should not appear ..
return clear_properties_iter (shape_type::text_ptr_array_type::tag (), ref.basic_iter (object_with_properties<shape_type::text_ptr_array_type>::tag ()));
case shape_type::UserObject:
return clear_properties_iter (shape_type::user_object_type::tag (), ref.basic_iter (object_with_properties<shape_type::user_object_type>::tag ()));
default:
return ref;
};
}
return ref;
}
Shapes::shape_type
Shapes::replace_prop_id (const Shapes::shape_type &ref, db::properties_id_type prop_id)
{
tl_assert (! ref.is_array_member ());
// nothing to do?
if (ref.has_prop_id () && ref.prop_id () == prop_id) {
return ref;
}
if (ref.has_prop_id ()) {
invalidate_prop_ids ();
// this assumes we can simply patch the properties ID ..
switch (ref.m_type) {
case shape_type::Null:
@ -1311,6 +1382,23 @@ Shapes::replace_prop_id_iter (typename db::object_tag<Sh>, const Iter &iter, db:
return shape_type (this, get_layer <db::object_with_properties <Sh>, db::stable_layer_tag> ().insert (wp));
}
template <class Sh, class Iter>
Shapes::shape_type
Shapes::clear_properties_iter (typename db::object_tag<Sh>, const Iter &iter)
{
if (manager () && manager ()->transacting ()) {
check_is_editable_for_undo_redo ();
db::layer_op<db::object_with_properties <Sh>, db::stable_layer_tag>::queue_or_append (manager (), this, false /*not insert*/, *iter);
}
Sh wop (*iter);
invalidate_state (); // HINT: must come before the change is done!
get_layer<db::object_with_properties <Sh>, db::stable_layer_tag> ().erase (iter);
if (manager () && manager ()->transacting ()) {
db::layer_op<Sh, db::stable_layer_tag>::queue_or_append (manager (), this, true /*insert*/, wop);
}
return shape_type (this, get_layer <Sh, db::stable_layer_tag> ().insert (wop));
}
template <class Sh1, class Sh2>
Shapes::shape_type
Shapes::reinsert_member_with_props (typename db::object_tag<Sh1>, const shape_type &ref, const Sh2 &sh)

View File

@ -1149,12 +1149,22 @@ public:
* of type object_with_properties<X>.
* This method is only allowed in editable mode.
*
* @param ref The shape reference which to replace the properties ID with
* @param ref The shape reference for which to replace the properties ID with
* @param prop_id The properties Id to replace.
* @return The reference to the new object
*/
shape_type replace_prop_id (const shape_type &ref, db::properties_id_type prop_id);
/**
* @brief Clears the user properties
*
* This method is only allowed in editable mode.
*
* @param ref The shape reference for which to clear the properties
* @return The reference to the new object
*/
shape_type clear_properties (const shape_type &ref);
/**
* @brief Replace an element by a given shape
*
@ -1638,6 +1648,9 @@ private:
template <class Sh, class Iter>
shape_type replace_prop_id_iter (typename db::object_tag<Sh>, const Iter &iter, db::properties_id_type prop_id);
template <class Sh, class Iter>
shape_type clear_properties_iter (typename db::object_tag<Sh>, const Iter &iter);
// A helper function to replace a shape given by a generic reference by doing an erase & insert
// Sh2 must not be a shape with properties
template <class Sh1, class Sh2>

View File

@ -1076,6 +1076,16 @@ static void set_cell_property (db::Cell *c, const tl::Variant &key, const tl::Va
c->prop_id (db::properties_id (props));
}
static void set_cell_properties (db::Cell *c, const std::map<tl::Variant, tl::Variant> &dict)
{
c->prop_id (db::properties_id (dict));
}
static void clear_cell_properties (db::Cell *c)
{
c->prop_id (0);
}
static tl::Variant get_cell_property (const db::Cell *c, const tl::Variant &key)
{
db::properties_id_type id = c->prop_id ();
@ -1831,6 +1841,22 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"\n"
"This method has been introduced in version 0.23."
) +
gsi::method_ext ("set_properties", &set_cell_properties, gsi::arg ("dict"),
"@brief Sets all user properties from the given dict\n"
"This method is a convenience method that replaces all user properties of the cell. Using that method is more "
"convenient than creating a new property set with a new ID and assigning that properties ID.\n"
"This method may change the properties ID. "
"Note: GDS only supports integer keys. OASIS supports numeric and string keys. "
"\n"
"This method has been introduced in version 0.30.3."
) +
gsi::method_ext ("clear_properties", &clear_cell_properties,
"@brief Clears all user properties\n"
"This method will remove all user properties. After it has been called, \\has_prop_id? will return false.\n"
"It is equivalent to setting the properties ID to zero.\n"
"\n"
"This method has been introduced in version 0.30.3."
) +
gsi::method_ext ("property", &get_cell_property, gsi::arg ("key"),
"@brief Gets the user property with the given key\n"
"This method is a convenience method that gets the property with the given key. "
@ -3539,6 +3565,18 @@ static void set_property (db::Instance *i, const tl::Variant &key, const tl::Var
set_prop_id (i, db::properties_id (props));
}
static void set_properties (db::Instance *inst, const std::map<tl::Variant, tl::Variant> &dict)
{
set_prop_id (inst, db::properties_id (dict));
}
static void clear_properties (db::Instance *inst)
{
tl_assert (inst->instances () != 0);
check_is_editable (inst->instances ());
*inst = inst->instances ()->clear_properties (*inst);
}
static tl::Variant get_property (const db::Instance *i, const tl::Variant &key)
{
db::properties_id_type id = i->prop_id ();
@ -4019,6 +4057,25 @@ Class<db::Instance> decl_Instance ("db", "Instance",
"\n"
"This method has been introduced in version 0.22."
) +
gsi::method_ext ("set_properties", &set_properties, gsi::arg ("dict"),
"@brief Sets all user properties from the given dict\n"
"This method is a convenience method that replaces all user properties of the instance. Using that method is more "
"convenient than creating a new property set with a new ID and assigning that properties ID.\n"
"This method may change the properties ID. "
"Note: GDS only supports integer keys. OASIS supports numeric and string keys. "
"Calling this method may invalidate any iterators. It should not be called inside a "
"loop iterating over instances.\n"
"\n"
"This method has been introduced in version 0.30.3."
) +
gsi::method_ext ("clear_properties", &clear_properties,
"@brief Clears all user properties\n"
"This method will remove all user properties. After it has been called, \\has_prop_id? will return false.\n"
"Calling this method may invalidate any iterators. It should not be called inside a "
"loop iterating over instances.\n"
"\n"
"This method has been introduced in version 0.30.3."
) +
gsi::method_ext ("property", &get_property, gsi::arg ("key"),
"@brief Gets the user property with the given key\n"
"This method is a convenience method that gets the property with the given key. "

View File

@ -323,6 +323,16 @@ static void set_layout_property (db::Layout *l, const tl::Variant &key, const tl
l->prop_id (db::properties_id (props));
}
static void set_layout_properties (db::Layout *l, const std::map<tl::Variant, tl::Variant> &dict)
{
l->prop_id (db::properties_id (dict));
}
static void clear_layout_properties (db::Layout *l)
{
l->prop_id (0);
}
static tl::Variant get_layout_property (const db::Layout *l, const tl::Variant &key)
{
db::properties_id_type id = l->prop_id ();
@ -579,21 +589,15 @@ static db::properties_id_type properties_id_from_list_meth (const db::Layout *,
return properties_id_from_list (properties);
}
static db::properties_id_type properties_id_from_hash (const std::map<tl::Variant, tl::Variant> &properties)
static db::properties_id_type properties_id_from_dict (const std::map<tl::Variant, tl::Variant> &properties)
{
db::PropertiesSet props;
for (std::map<tl::Variant, tl::Variant>::const_iterator v = properties.begin (); v != properties.end (); ++v) {
props.insert (v->first, v->second);
}
return db::properties_id (props);
return db::properties_id (properties);
}
// for backward compatibility
static db::properties_id_type properties_id_from_hash_meth (const db::Layout *, const std::map<tl::Variant, tl::Variant> &properties)
static db::properties_id_type properties_id_from_dict_meth (const db::Layout *, const std::map<tl::Variant, tl::Variant> &properties)
{
return properties_id_from_hash (properties);
return properties_id_from_dict (properties);
}
static tl::Variant get_properties_list (db::properties_id_type id)
@ -607,13 +611,13 @@ static tl::Variant get_properties_list_meth (const db::Layout *, db::properties_
return db::properties (id).to_list_var ();
}
static tl::Variant get_properties_hash (db::properties_id_type id)
static tl::Variant get_properties_dict (db::properties_id_type id)
{
return db::properties (id).to_dict_var ();
}
// for backward compatibility
static tl::Variant get_properties_hash_meth (const db::Layout *, db::properties_id_type id)
static tl::Variant get_properties_dict_meth (const db::Layout *, db::properties_id_type id)
{
return db::properties (id).to_dict_var ();
}
@ -1320,6 +1324,22 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"\n"
"This method has been introduced in version 0.24."
) +
gsi::method_ext ("set_properties", &set_layout_properties, gsi::arg ("dict"),
"@brief Sets all user properties from the given dict\n"
"This method is a convenience method that replaces all user properties of the layout object. Using that method is more "
"convenient than creating a new property set with a new ID and assigning that properties ID.\n"
"This method may change the properties ID. "
"Note: GDS only supports integer keys. OASIS supports numeric and string keys. "
"\n"
"This method has been introduced in version 0.30.3."
) +
gsi::method_ext ("clear_properties", &clear_layout_properties,
"@brief Clears all user properties\n"
"This method will remove all user properties. After it has been called, \\has_prop_id? will return false.\n"
"It is equivalent to setting the properties ID to zero.\n"
"\n"
"This method has been introduced in version 0.30.3."
) +
gsi::method_ext ("property", &get_layout_property, gsi::arg ("key"),
"@brief Gets the Layout's user property with the given key\n"
"This method is a convenience method that gets the property with the given key. "
@ -1364,10 +1384,10 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"@return The unique properties ID for that set"
) +
// backward-compatible version as method
gsi::method_ext ("properties_id", &properties_id_from_hash_meth, gsi::arg ("properties"),
gsi::method_ext ("properties_id", &properties_id_from_dict_meth, gsi::arg ("properties"),
"@hide"
) +
gsi::method ("properties_id", &properties_id_from_hash, gsi::arg ("properties"),
gsi::method ("properties_id", &properties_id_from_dict, gsi::arg ("properties"),
"@brief Gets the properties ID for a given properties set\n"
"\n"
"This variant accepts a hash of value vs. key for the properties instead of array of key/value pairs. "
@ -1413,10 +1433,10 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"In version 0.30, this method was turned into a static (class method), providing universal conversions without need for a Layout object."
) +
// backward-compatible version as method
gsi::method_ext ("properties_hash", &get_properties_hash_meth, gsi::arg ("properties_id"),
gsi::method_ext ("properties_hash", &get_properties_dict_meth, gsi::arg ("properties_id"),
"@hide"
) +
gsi::method ("properties_hash", &get_properties_hash, gsi::arg ("properties_id"),
gsi::method ("properties_hash", &get_properties_dict, gsi::arg ("properties_id"),
"@brief Gets the properties set for a given properties ID as a hash\n"
"\n"
"Returns the properties for a given properties ID as a hash.\n"

View File

@ -954,6 +954,17 @@ static void set_property (db::Shape *s, const tl::Variant &key, const tl::Varian
set_prop_id (s, db::properties_id (props));
}
static void set_properties (db::Shape *shape, const std::map<tl::Variant, tl::Variant> &dict)
{
set_prop_id (shape, db::properties_id (dict));
}
static void clear_properties (db::Shape *shape)
{
db::Shapes *shapes = shapes_checked (shape);
*shape = shapes->clear_properties (*shape);
}
static tl::Variant get_property (const db::Shape *s, const tl::Variant &key)
{
db::properties_id_type id = s->prop_id ();
@ -1312,6 +1323,25 @@ Class<db::Shape> decl_Shape ("db", "Shape",
"\n"
"This method has been introduced in version 0.22."
) +
gsi::method_ext ("set_properties", &set_properties, gsi::arg ("dict"),
"@brief Sets all user properties from the given dict\n"
"This method is a convenience method that replaces all user properties of the shape. Using that method is more "
"convenient than creating a new property set with a new ID and assigning that properties ID.\n"
"This method may change the properties ID. "
"Note: GDS only supports integer keys. OASIS supports numeric and string keys. "
"Calling this method may invalidate any iterators. It should not be called inside a "
"loop iterating over instances.\n"
"\n"
"This method has been introduced in version 0.30.3."
) +
gsi::method_ext ("clear_properties", &clear_properties,
"@brief Clears all user properties\n"
"This method will remove all user properties. After it has been called, \\has_prop_id? will return false.\n"
"Calling this method may invalidate any iterators. It should not be called inside a "
"loop iterating over instances.\n"
"\n"
"This method has been introduced in version 0.30.3."
) +
gsi::method_ext ("property", &get_property, gsi::arg ("key"),
"@brief Gets the user property with the given key\n"
"This method is a convenience method that gets the property with the given key. "

View File

@ -2421,7 +2421,7 @@ TEST(12A)
shape = topcell.shapes (lindex).begin (db::Shapes::shape_iterator::All);
while (! shape.at_end ()) {
if (shape->with_props ()) {
if (shape->has_prop_id ()) {
topcell.shapes (lindex).replace_prop_id (*shape, shape->prop_id () + 100);
}
++shape;
@ -2494,7 +2494,7 @@ TEST(12C)
shape = topcell.shapes (lindex).begin (db::Shapes::shape_iterator::All);
while (! shape.at_end ()) {
if (shape->with_props ()) {
if (shape->has_prop_id ()) {
topcell.shapes (lindex).replace_prop_id (*shape, shape->prop_id () + 100);
}
++shape;
@ -2578,7 +2578,7 @@ TEST(12E)
shape = topcell.shapes (lindex).begin (db::Shapes::shape_iterator::All);
while (! shape.at_end ()) {
if (shape->with_props ()) {
if (shape->has_prop_id ()) {
topcell.shapes (lindex).replace_prop_id (*shape, shape->prop_id () + 100);
}
++shape;
@ -2677,7 +2677,7 @@ TEST(12G)
shape = topcell.shapes (lindex).begin (db::Shapes::shape_iterator::All);
while (! shape.at_end ()) {
if (shape->with_props ()) {
if (shape->has_prop_id ()) {
topcell.shapes (lindex).replace_prop_id (*shape, shape->prop_id () + 100);
}
++shape;
@ -2774,7 +2774,7 @@ TEST(12I)
shape = topcell.shapes (lindex).begin (db::Shapes::shape_iterator::All);
while (! shape.at_end ()) {
if (shape->with_props ()) {
if (shape->has_prop_id ()) {
topcell.shapes (lindex).replace_prop_id (*shape, shape->prop_id () + 100);
}
++shape;
@ -2834,7 +2834,7 @@ TEST(12J)
shape = topcell.shapes (lindex).begin (db::Shapes::shape_iterator::All);
while (! shape.at_end ()) {
if (shape->with_props ()) {
if (shape->has_prop_id ()) {
topcell.shapes (lindex).replace_prop_id (*shape, shape->prop_id () + 100);
}
++shape;

View File

@ -905,6 +905,41 @@ class DBInstance_TestClass < TestBase
end
# User properties
def test_8_UserProperties
ly = RBA::Layout::new
ci1 = ly.add_cell("c1")
ci2 = ly.add_cell("c2")
c1 = ly.cell(ci1)
c2 = ly.cell(ci2)
inst = RBA::CellInstArray::new(c1.cell_index, RBA::Trans::new)
i = c2.insert(inst)
assert_equal(i.property("k").inspect, "nil")
assert_equal(i.properties.inspect, "{}")
i.set_property("k", 17)
assert_equal(i.property("k").inspect, "17")
assert_equal(i.property("u").inspect, "nil")
assert_equal(i.properties.inspect, "{\"k\"=>17}")
i.set_property("u", "42")
assert_equal(i.properties.inspect, "{\"k\"=>17, \"u\"=>\"42\"}")
i.set_properties({ "a" => 17, 42 => "u" })
assert_equal(i.properties, {42=>"u", "a"=>17})
assert_equal(i.has_prop_id?, true)
i.clear_properties
assert_equal(i.properties, {})
assert_equal(i.has_prop_id?, false)
end
end
load("test_epilogue.rb")

View File

@ -715,6 +715,12 @@ class DBLayoutTests2_TestClass < TestBase
c1.delete_property( 17 )
assert_equal( c1.property( 17 ).inspect, "nil" )
assert_equal( c1.property( 5 ).inspect, "23" )
c1.set_properties({ "a" => 17, 42 => "u" })
assert_equal(c1.properties, {42=>"u", "a"=>17})
assert_equal(c1.has_prop_id?, true)
c1.clear_properties
assert_equal(c1.properties, {})
assert_equal(c1.has_prop_id?, false)
end
@ -1057,6 +1063,13 @@ class DBLayoutTests2_TestClass < TestBase
ly.delete_property("x")
assert_equal(ly.property("x"), nil)
ly.set_properties({ "a" => 17, 42 => "u" })
assert_equal(ly.properties, {42=>"u", "a"=>17})
assert_equal(ly.has_prop_id?, true)
ly.clear_properties
assert_equal(ly.properties, {})
assert_equal(ly.has_prop_id?, false)
end
# Meta information

View File

@ -1683,6 +1683,13 @@ class DBShapes_TestClass < TestBase
sh.set_property("u", "42")
assert_equal(sh.properties.inspect, "{\"k\"=>17, \"u\"=>\"42\"}")
sh.set_properties({ "a" => 17, 42 => "u" })
assert_equal(sh.properties, {42=>"u", "a"=>17})
assert_equal(sh.has_prop_id?, true)
sh.clear_properties
assert_equal(sh.properties, {})
assert_equal(sh.has_prop_id?, false)
end
# Shape objects with properties