Fixed multiple dispatch issues on io.jl

Related to issue #74, io.jl is no more modifying functionality of
LightXML
This commit is contained in:
Jukka Aho
2017-01-21 10:30:23 +02:00
committed by ovainola
parent c1b5d2f7ed
commit 9b4c1a4c5e
5 changed files with 215 additions and 313 deletions
+1 -1
View File
@@ -30,7 +30,7 @@ module Testing
end
include("io.jl")
export Xdmf, h5file, xmffile, has_child, get_child, new_dataitem
export Xdmf, h5file, xmffile, xdmf_filter, new_dataitem, get_temporal_collection
include("fields.jl")
export Field, DCTI, DVTI, DCTV, DVTV, CCTI, CVTI, CCTV, CVTV, Increment
+170 -112
View File
@@ -3,100 +3,6 @@
using HDF5
using LightXML
importall LightXML
importall Base
function new_element(name::String, attrs::Dict)
x = new_element(name)
for (k, v) in attrs
x[k] = v
end
return x
end
function setindex!(x::XMLElement, content::Any, attr_name::String)
set_attribute(x, attr_name, content)
end
function haskey(x::XMLElement, key::String)
return has_child(x, key) || has_attribute(x, key)
end
function has_child(x::XMLElement, child_name::String)
return get_child(x, child_name) != nothing
end
function get_attribute(x::XMLElement, attr_name::String)
attr = attribute(x, attr_name)
numeric = tryparse(Int64, attr)
isnull(numeric) && (numeric = tryparse(Float64, attr))
isnull(numeric) && return attr
return get(numeric)
end
function new_child(xparent::XMLElement, name::String, attrs::Dict)
x = new_child(xparent, name)
for (k, v) in attrs
x[k] = v
end
return x
end
""" Basic traverse support, so that it's possible to find data from xml using
path syntax e.g. /foo/bar[2]/baz[@Name=Frame 1]/DataItem. If several elements
with same name exists in tree, pick first by default and next ones can be picked
using [] syntax or [@attr=value] syntax, see [1] for details. For last item use
[end].
[1] http://www.xdmf.org/index.php/XDMF_Model_and_Format
"""
function get_child(x::XMLElement, child_name::String)
'/' in child_name && return nothing
m = match(r"(\w+)\[(.+)\]", child_name)
if m != nothing
child_name = m[1]
end
childs = []
for child in child_elements(x)
if name(child) == child_name
push!(childs, child)
end
end
length(childs) == 0 && return nothing
m == nothing && return first(childs)
j = tryparse(Int, m[2])
isnull(j) || return childs[get(j)]
m[2] == "end" && return childs[end]
m2 = match(r"@(.+)=(.+)", m[2])
m2 == nothing && throw("Unable to parse: $(m[2])")
attr_name = convert(String, m2[1])
attr_value = convert(String, m2[2])
for child in childs
has_attribute(child, attr_name) || continue
if get_attribute(child, attr_name) == attr_value
return child
end
end
throw("Unable to parse: $(m[2])")
end
function getindex(x::XMLElement, attr_name::String)
attr_name = strip(attr_name, '/')
child = get_child(x, attr_name)
child == nothing || return child
has_attribute(x, attr_name) && return get_attribute(x, attr_name)
if '/' in attr_name
items = map(String, split(attr_name, '/'))
attr_name = first(items)
length(items) > 1 || throw(KeyError(attr_name))
haskey(x, attr_name) || throw(KeyError(attr_name))
path = join(items[2:end], '/')
new_item = getindex(x, attr_name)
return new_item[path]
else
throw(KeyError(attr_name))
end
end
type Xdmf
name :: String
@@ -104,24 +10,6 @@ type Xdmf
hdf :: HDF5File
end
function new_child(xdmf::Xdmf, args...; kwargs...)
new_child(xdmf.xml, args...; kwargs...)
end
function read(xdmf::Xdmf, path::String)
result = getindex(xdmf.xml, path)
if endswith(path, "DataItem")
format = get_attribute(result, "Format")
@assert format == "HDF"
h5file, path = map(String, split(content(result), ':'))
h5file = dirname(xdmf.name) * "/" * h5file
isfile(h5file) || throw("Xdmf: h5 file $h5file not found!")
return read(xdmf.hdf, path)
else
return result
end
end
function Xdmf()
return Xdmf(tempname())
end
@@ -134,6 +22,7 @@ function xmffile(xdmf::Xdmf)
return xdmf.name*".xmf"
end
""" Initialize a new Xdmf object. """
function Xdmf(name::String; overwrite=false)
xdmf = new_element("Xdmf")
h5file = "$name.h5"
@@ -164,6 +53,175 @@ function Xdmf(name::String; overwrite=false)
return Xdmf(name, xdmf, hdf)
end
""" Return the basic structure of Xdmf document. Creates a new TemporalCollection if not found.
Basic structure for XML part of Xdmf file is
<?xml version="1.0" encoding="utf-8"?>
<Xdmf xmlns:xi="http://www.w3.org/2001/XInclude" Version="2.1">
<Domain>
<Grid CollectionType="Temporal" GridType="Collection">
</Grid>
</Domain>
</Xdmf>
"""
function get_temporal_collection(xdmf::Xdmf)
domain = find_element(xdmf.xml, "Domain")
grid = nothing
if domain == nothing
debug("Xdmf: creating new temporal collection")
domain = new_child(xdmf.xml, "Domain")
grid = new_child(domain, "Grid")
set_attribute(grid, "CollectionType", "Temporal")
set_attribute(grid, "GridType", "Collection")
end
grid = find_element(domain, "Grid")
return grid
end
""" Returns some spesific child xml element from a array of XMLElement based on,
"Xdmf extensions" see [1] for details.
Parameters
----------
child_elements :: Vector{XMLElement}
A vector of XMLElements where to perform filtering.
child_name :: String
Child element name, maybe containing Xdmf instructions
Returns
-------
nothing if nothing is found, otherwise XMLElement matching to filtering
Examples
--------
julia> grid1 = new_element("Grid")
julia> add_text(grid1, "I am first grid")
julia> grid2 = new_element("Grid")
julia> add_text(grid2, "I am second grid")
julia> set_attribute(grid2, "Name", "Frame 2")
julia> grid3 = new_element("Grid")
julia> add_text(grid3, "I am third grid")
julia> grids = [grid1, grid2, grid3]
To return second Grid element, one can use
julia> xdmf_filter(grids, "Grid[2]")
To return Grid which has attribute Name="Frame 2", use
julia> xdmf_filter(grids, "Grid[@name=Frame 2]")
To pick last Grid, use [end], e.g.
julia> xdmf_filter(grids, "Grid[end]").
References
----------
[1] http://www.xdmf.org/index.php/XDMF_Model_and_Format
"""
function xdmf_filter(child_elements, child_name)
if '/' in child_name # needs path traversal
return nothing
end
# filter children elements using syntax child[X] -> rename child_name
m = match(r"(\w+)\[(.+)\]", child_name)
if m != nothing
child_name = m[1]
end
# first find any relevant child elements (has same tag)
childs = []
for child in child_elements
if LightXML.name(child) == child_name
push!(childs, child)
end
end
# childs not found at all
length(childs) == 0 && return nothing
# by default return first
m == nothing && return first(childs)
# if [end] return last
m[2] == "end" && return childs[end]
# otherwise try parse int and return nth children from list
parsed_int = tryparse(Int, m[2])
if !isnull(parsed_int)
idx = get(parsed_int)
if (idx > 0) && (idx <= length(childs))
return childs[idx]
else
# wrong index
return nothing
end
end
# [X] is something else than integer, filter children elements using syntax child[@attr=value]
m2 = match(r"@(.+)=(.+)", m[2])
m2 == nothing && throw("Unable to parse: $(m[2])")
attr_name = convert(String, m2[1])
attr_value = convert(String, m2[2])
for child in childs
has_attribute(child, attr_name) || continue
if attribute(child, attr_name) == attr_value
return child
end
end
# nothing found
return nothing
end
""" Traverse XML path. Xdmf filtering can be used, so it's possible to find
data from xml using syntax e.g.
julia> traverse(xdmf, x, "/Domain/Grid[2]/Grid[@Name=Frame 1]/DataItem")
"""
function traverse(xdmf::Xdmf, x::XMLElement, attr_name::String)
attr_name = strip(attr_name, '/')
if has_attribute(x, attr_name)
return attribute(x, attr_name)
end
childs = child_elements(x)
if '/' in attr_name
items = split(attr_name, '/')
new_item = xdmf_filter(childs, first(items))
new_path = join(items[2:end], '/')
return traverse(xdmf, new_item, new_path)
end
child = xdmf_filter(childs, attr_name)
return child
end
""" Read data from Xdmf file.
Traversing is supported, so one can easily traverse XML tree e.g.
julia> read(xdmf, "/Domain/Grid/Grid[2]/Geometry")
"""
function read(xdmf::Xdmf, path::String)
result = traverse(xdmf, xdmf.xml, path)
if endswith(path, "DataItem")
format = attribute(result, "Format"; required=true)
@assert format == "HDF"
h5file, path = map(String, split(content(result), ':'))
h5file = dirname(xdmf.name) * "/" * h5file
isfile(h5file) || throw("Xdmf: h5 file $h5file not found!")
return read(xdmf.hdf, path)
else
return result
end
end
function save!(xdmf::Xdmf)
doc = XMLDocument()
set_root(doc, xdmf.xml)
+4 -16
View File
@@ -488,20 +488,6 @@ function (solver::Solver)(field_name::AbstractString, time::Float64)
return merge(fields...)
end
function get_temporal_collection(xdmf::Xdmf)
domain = find_element(xdmf.xml, "Domain")
grid = nothing
if domain == nothing
info("Xdmf: creating new temporal collection")
domain = new_child(xdmf.xml, "Domain")
grid = new_child(domain, "Grid")
set_attribute(grid, "CollectionType", "Temporal")
set_attribute(grid, "GridType", "Collection")
end
grid = find_element(domain, "Grid")
return grid
end
""" Default update for solver. """
function update!{S}(solver::Solver{S}; show_info=true)
u = solver.u
@@ -540,7 +526,8 @@ function update_xdmf!{S}(solver::Solver{S}; show_info=true)
geom_type = (ndim == 2 ? "XY" : "XYZ")
data_node_ids = new_dataitem(xdmf, "/Node IDs", node_ids)
data_geometry = new_dataitem(xdmf, "/Geometry", X)
geometry = new_element("Geometry", Dict("Type" => geom_type))
geometry = new_element("Geometry")
set_attribute(geometry, "Type", geom_type)
add_child(geometry, data_geometry)
# 2. save topology
@@ -588,7 +575,8 @@ function update_xdmf!{S}(solver::Solver{S}; show_info=true)
# 3. save solved field
frame = new_element("Grid")
new_child(frame, "Time", Dict("Value" => solver.time))
time = new_child(frame, "Time")
set_attribute(time, "Value", solver.time)
add_child(frame, geometry)
for topo in topology
add_child(frame, topo)
+4 -2
View File
@@ -420,10 +420,12 @@ function update_xdmf!(solver::Solver{Modal}; show_info=true)
info("Creating frequency frame f=$(round(freq, 3)), path=$path")
frame = new_element("Grid")
new_child(frame, "Time", Dict("Value" => freq))
time = new_child(frame, "Time")
set_attribute(time, "Value", freq)
# add geometry
geometry = new_element("Geometry", Dict("Type" => geom_type))
geometry = new_element("Geometry")
set_attribute(geometry, "Type", geom_type)
data_node_ids = new_dataitem(xdmf, "/Node IDs", node_ids)
data_geometry = new_dataitem(xdmf, "/Geometry", X)
add_child(geometry, data_geometry)