///|
fn json_escape(text : String) -> String {
let output = StringBuilder()
for character in text {
match character {
'"' => output <+ "\\\""
'\\' => output <+ "\\\\"
'\n' => output <+ "\\n"
'\r' => output <+ "\\r"
'\t' => output <+ "\\t"
character if character.to_int() < 0x20 => output <+ "?"
character => output.write_char(character)
}
}
output.to_string()
}
///|
fn json_string(text : String) -> String {
"\"" + json_escape(text) + "\""
}
///|
fn join_json(values : Array[String]) -> String {
values.join(",")
}
///|
fn value_json(value : Value) -> String {
match value {
Null => "null"
Omitted => "\"*\""
Integer(number) => number.to_string()
Real(number) => number.to_string()
String(text) => json_string(text)
Enumeration(text) => json_string("." + text + ".")
Reference(id) => "{\"ref\":" + id.to_string() + "}"
Binary(text) => json_string(text)
Typed(name~, value~) =>
"{\"type\":" + json_string(name) + ",\"value\":" + value_json(value) + "}"
List(values) => "[" + join_json(values.map(value_json)) + "]"
}
}
///|
pub fn value_to_json(value : Value) -> String {
value_json(value)
}
///|
pub fn entity_to_json(entity : Entity) -> String {
"{\"id\":" +
entity.id.to_string() +
",\"type\":" +
json_string(entity.name) +
",\"arguments\":[" +
join_json(entity.arguments.map(value_json)) +
"]}"
}
///|
fn optional_string_json(value : String?) -> String {
match value {
Some(text) => json_string(text)
None => "null"
}
}
///|
fn optional_int_json(value : Int?) -> String {
match value {
Some(number) => number.to_string()
None => "null"
}
}
///|
fn product_kind_json(kind : ProductKind) -> String {
match kind {
Project => "project"
Site => "site"
Building => "building"
Storey => "storey"
Space => "space"
Wall => "wall"
Door => "door"
Window => "window"
Beam => "beam"
Column => "column"
Slab => "slab"
Roof => "roof"
Stair => "stair"
Ramp => "ramp"
Opening => "opening"
Generic => "generic"
}
}
///|
pub fn product_to_json(product : Product) -> String {
"{\"id\":" +
product.id.to_string() +
",\"type\":" +
json_string(product.ifc_type) +
",\"kind\":" +
json_string(product_kind_json(product.kind)) +
",\"global_id\":" +
optional_string_json(product.global_id) +
",\"name\":" +
optional_string_json(product.name) +
",\"description\":" +
optional_string_json(product.description) +
",\"object_type\":" +
optional_string_json(product.object_type) +
",\"placement\":" +
optional_int_json(product.placement) +
",\"representation\":" +
optional_int_json(product.representation) +
",\"tag\":" +
optional_string_json(product.tag) +
",\"property_sets\":[" +
product.property_sets.map(value => value.to_string()).join(",") +
"]" +
",\"container\":" +
optional_int_json(product.container) +
"}"
}
///|
pub fn property_to_json(property : Property) -> String {
"{\"name\":" +
json_string(property.name) +
",\"description\":" +
optional_string_json(property.description) +
",\"value\":" +
value_json(property.value) +
",\"unit\":" +
optional_int_json(property.unit) +
"}"
}
///|
pub fn property_set_to_json(property_set : PropertySet) -> String {
"{\"id\":" +
property_set.id.to_string() +
",\"name\":" +
optional_string_json(property_set.name) +
",\"description\":" +
optional_string_json(property_set.description) +
",\"properties\":[" +
join_json(property_set.properties.map(property_to_json)) +
"]}"
}
///|
fn point_json(point : Point3) -> String {
"[" +
point.x.to_string() +
"," +
point.y.to_string() +
"," +
point.z.to_string() +
"]"
}
///|
pub fn geometry_to_json(geometry : Geometry) -> String {
let kind = match geometry.kind {
Extrusion => "extrusion"
FacetedBrep => "faceted_brep"
TriangulatedMesh => "triangulated_mesh"
CsgSolid => "csg_solid"
Profile => "profile"
Polyline => "polyline"
PointCloud => "point_cloud"
Unknown => "unknown"
}
"{\"source_id\":" +
geometry.source_id.to_string() +
",\"kind\":" +
json_string(kind) +
",\"points\":[" +
join_json(geometry.points.map(point_json)) +
"]" +
",\"references\":[" +
geometry.references.map(value => value.to_string()).join(",") +
"]" +
",\"depth\":" +
geometry.depth.map(Double::to_string).unwrap_or("null") +
",\"profile_width\":" +
geometry.profile_width.map(Double::to_string).unwrap_or("null") +
",\"profile_height\":" +
geometry.profile_height.map(Double::to_string).unwrap_or("null") +
",\"profile_radius\":" +
geometry.profile_radius.map(Double::to_string).unwrap_or("null") +
",\"face_count\":" +
geometry.face_count.to_string() +
",\"triangle_count\":" +
geometry.triangle_count.to_string() +
"}"
}
///|
fn spatial_to_json(node : SpatialNode) -> String {
"{\"id\":" +
node.id.to_string() +
",\"type\":" +
json_string(node.ifc_type) +
",\"name\":" +
optional_string_json(node.name) +
",\"products\":[" +
node.products.map(value => value.to_string()).join(",") +
"]" +
",\"children\":[" +
join_json(node.children.map(spatial_to_json)) +
"]}"
}
///|
pub fn model_to_json(model : Model) -> String raise ParseError {
let index = model.index()
let products = extract_products(index)
let property_sets = all_property_sets(index)
let geometries = geometry_entities(index).map(entity => {
geometry_for_entity(index, entity)
})
let roots = spatial_roots(index)
"{\"schema\":" +
json_string(model.schema()) +
",\"header\":{\"file_name\":" +
optional_string_json(model.header.file_name) +
",\"time_stamp\":" +
optional_string_json(model.header.time_stamp) +
",\"description\":[" +
model.header.description.map(json_string).join(",") +
",\"schemas\":[" +
model.header.schema_identifiers.map(json_string).join(",") +
"]}" +
",\"entities\":[" +
join_json(model.entities.map(entity_to_json)) +
"]" +
",\"products\":[" +
join_json(products.map(product_to_json)) +
"]" +
",\"property_sets\":[" +
join_json(property_sets.map(property_set_to_json)) +
"]" +
",\"geometry\":[" +
join_json(geometries.map(geometry_to_json)) +
"]" +
",\"spatial\":[" +
join_json(roots.map(spatial_to_json)) +
"]}"
}