+ # maximum number of elements allowed in a changeset
+ MAX_ELEMENTS = 10000
+
+ # maximum time a changeset is allowed to be open for.
+ MAX_TIME_OPEN = 1.day
+
+ # idle timeout increment, one hour seems reasonable.
+ IDLE_TIMEOUT = 1.hour
+
+ # Use a method like this, so that we can easily change how we
+ # determine whether a changeset is open, without breaking code in at
+ # least 6 controllers
+ def is_open?
+ # a changeset is open (that is, it will accept further changes) when
+ # it has not yet run out of time and its capacity is small enough.
+ # note that this may not be a hard limit - due to timing changes and
+ # concurrency it is possible that some changesets may be slightly
+ # longer than strictly allowed or have slightly more changes in them.
+ ((closed_at > Time.now.getutc) && (num_changes <= MAX_ELEMENTS))
+ end
+
+ def set_closed_time_now
+ self.closed_at = Time.now.getutc if is_open?
+ end
+
+ def self.from_xml(xml, create = false)
+ p = XML::Parser.string(xml, :options => XML::Parser::Options::NOERROR)
+ doc = p.parse
+
+ doc.find("//osm/changeset").each do |pt|
+ return Changeset.from_xml_node(pt, create)
+ end
+ raise OSM::APIBadXMLError.new("changeset", xml, "XML doesn't contain an osm/changeset element.")
+ rescue LibXML::XML::Error, ArgumentError => ex
+ raise OSM::APIBadXMLError.new("changeset", xml, ex.message)
+ end
+
+ def self.from_xml_node(pt, create = false)
+ cs = Changeset.new
+ if create
+ cs.created_at = Time.now.getutc
+ # initial close time is 1h ahead, but will be increased on each
+ # modification.
+ cs.closed_at = cs.created_at + IDLE_TIMEOUT
+ # initially we have no changes in a changeset
+ cs.num_changes = 0
+ end
+
+ pt.find("tag").each do |tag|
+ raise OSM::APIBadXMLError.new("changeset", pt, "tag is missing key") if tag["k"].nil?
+ raise OSM::APIBadXMLError.new("changeset", pt, "tag is missing value") if tag["v"].nil?
+ cs.add_tag_keyval(tag["k"], tag["v"])