Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file piqobj_of_xml.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342(*
Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2017 Anton Lavrik
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*)moduleC=Piqi_commonopenCopenPiqobj_commontypexml=Piqi_xml_type.xmltypexml_elem=Piqi_xml_type.xml_elemlethandle_unknown_field=Piqobj_of_json.handle_unknown_fieldletcheck_duplicate=Piqobj_of_piq.check_duplicateletparse_scalarxml_elemerr_string=let_name,l=xml_eleminmatchlwith|[`Datas]->s|_->errorxml_elemerr_stringletparse_string_scalarxml_elemerr_string=let_name,l=xml_eleminmatchlwith|[]->(* empty element content means empty string *)letres=""inPiqloc.addrefretxml_elemres|[`Datas]->s|_->errorxml_elemerr_stringletparse_intxml_elem=lets=parse_scalarxml_elem"int constant expected"intrymatchs.[0]with|'-'->`int(Int64.of_strings)(* negative integer *)|_->`uint(Piq_parser.parse_uints)withFailure_->(* NOTE: actually, there can be two errors here: invalid integer literal
* and integer overflow *)errorxml_elem"invalid int constant"letparse_floatxml_elem=lets=parse_scalarxml_elem"float constant expected"inmatchswith|"NaN"->Pervasives.nan|"Infinity"->Pervasives.infinity|"-Infinity"->Pervasives.neg_infinity|_->tryfloat_of_stringswithFailure_->errorxml_elem"invalid float constant"letparse_boolxml_elem=leterr="bool constant expected"inlets=parse_scalarxml_elemerrinmatchswith|"true"->true|"false"->false|_->errorxml_elemerrletparse_stringxml_elem=parse_string_scalarxml_elem"string constant expected"letparse_binaryxml_elem=lets=parse_string_scalarxml_elem"binary constant expected"intryPiqi_base64.decodeswithInvalid_argument_->errorxml_elem"invalid base64-encoded string"(* get the list of XML elements from the node *)letget_record_elements(l:xmllist):xml_elemlist=List.map(funxml->matchxmlwith|`Elemx->x|`Datas->errorxml"XML element is expected as a record field")lletrecparse_obj(t:T.piqtype)(x:xml_elem):Piqobj.obj=matchtwith(* built-in types *)|`int->parse_intx|`float->`float(parse_floatx)|`bool->`bool(parse_boolx)|`string->`string(parse_stringx)|`binary->`binary(parse_binaryx)|`any->`any(parse_anyx)(* custom types *)|`recordt->`record(parse_recordtx)|`variantt->`variant(parse_varianttx)|`enumt->`enum(parse_enumtx)|`listt->`list(parse_listtx)|`aliast->`alias(parse_aliastx)andparse_anyxml_elem=matchxml_elemwith|_name,(`Elem("piqi-type",[`Data"piqi-any"]))::rem->(* extended piqi-any format *)letrem=get_record_elementsremin(* manually parsing the piqi-any record, so that we could extract the
* symbolic xml representation *)(* XXX: check correspondence between typed protobuf and typed xml? *)lettypename_obj,rem=parse_optional_field"type"`stringNonereminletprotobuf_obj,rem=parse_optional_field"protobuf"`binaryNonereminletxml_obj,rem=parse_optional_field"xml"`anyNonereminletpiq_obj,rem=parse_optional_field"piq"`stringNoneremin(* issue warnings on unparsed fields *)List.iterhandle_unknown_fieldrem;lettypename=matchtypename_objwith|Some(`stringx)->Somex|_->Noneinletprotobuf=matchprotobuf_objwith|Some(`binaryx)->Somex|_->Noneinletxml_ast=matchxml_objwith|Some(`any{Any.xml_ast=xml_ast})->xml_ast|_->Noneinletpiq_ast=matchpiq_objwith|Some(`stringx)->letpiq_ast=!Piqobj.piq_of_stringxinSomepiq_ast|_->NoneinAny.({Piqobj.default_anywithtypename=typename;pb=protobuf;xml_ast=xml_ast;piq_ast=piq_ast;})|_->(* regular symbolic piqi-any *)Any.({Piqobj.default_anywithxml_ast=Somexml_elem;})andparse_recordtxml_elem=debug"do_parse_record: %s\n"(some_oft.T.Record.name);(* get the list of XML elements from the node *)let_name,l=xml_eleminletl=get_record_elementslin(* NOTE: passing locating information as a separate parameter since empty
* list is unboxed and doesn't provide correct location information *)letloc=xml_eleminletfields_spec=t.T.Record.fieldinletfields,rem=List.fold_left(parse_fieldloc)([],l)fields_specin(* issue warnings on unparsed fields *)List.iterhandle_unknown_fieldrem;(* put required fields back at the top *)R.({t=t;field=List.revfields;unparsed_piq_fields_ref=None})andparse_fieldloc(accu,rem)t=letfields,rem=do_parse_fieldloctremin(List.rev_appendfieldsaccu,rem)anddo_parse_fieldloctl=letopenT.Fieldinletname=C.name_of_fieldtindebug"do_parse_field: %s\n"name;letfield_type=some_oft.piqtypeinletvalues,rem=matcht.modewith|`required->letx,rem=parse_required_fieldlocnamefield_typelin[x],rem|`optional->letx,rem=parse_optional_fieldnamefield_typet.defaultlinletres=(matchxwithSomex->[x]|None->[])inres,rem|`repeated->parse_repeated_fieldnamefield_typelinletfields=List.map(funx->F.({t=t;obj=Somex}))valuesinfields,remandparse_required_fieldlocnamefield_typel=letres,rem=find_fieldsnamelinmatchreswith|[]->errorloc("missing field "^U.quotename)|x::tail->check_duplicatenametail;parse_objfield_typex,rem(* find field by name, return found fields and remaining fields *)andfind_fields(name:string)(l:xml_elemlist):(xml_elemlist*xml_elemlist)=letrecauxaccurem=function|[]->List.revaccu,List.revrem|((n,_)ash)::twhenn=name->aux(h::accu)remt|h::t->auxaccu(h::rem)tinaux[][]landparse_optional_fieldnamefield_typedefaultl=letres,rem=find_fieldsnamelinmatchreswith|[]->Piqobj_common.parse_defaultfield_typedefault,rem|x::tail->check_duplicatenametail;Some(parse_objfield_typex),rem(* parse repeated variant field allowing variant names if field name is
* unspecified *)andparse_repeated_fieldnamefield_typel=letres,rem=find_fieldsnamelinmatchreswith|[]->[],rem(* allowing repeated field to be actually missing *)|l->letres=List.map(parse_objfield_type)linres,remandparse_varianttxml_elem=debug"parse_variant: %s\n"(some_oft.T.Variant.name);let_name,l=xml_eleminmatchlwith|[`Elem((name,_)asxml_elem)]->letoptions=t.T.Variant.optioninletoption=tryleto=List.find(funo->name=C.name_of_optiono)optionsinparse_optionoxml_elemwithNot_found->errorxml_elem("unknown variant option: "^U.quotename)inV.({t=t;option=option})|_->errorxml_elem"exactly one XML element expected as a variant value"andparse_optiontxml_elem=letopenT.Optioninletname,l=xml_eleminmatcht.piqtype,lwith|None,[]->O.({t=t;obj=None})|None,_->errorname("no value expected for option flag "^U.quotename)|Someoption_type,_->letobj=parse_objoption_typexml_eleminO.({t=t;obj=Someobj})andparse_enumtxml_elem=debug"parse_enum: %s\n"(some_oft.T.Enum.name);letname=parse_scalarxml_elem"exactly one XML CDATA expected as an enum value"inletoptions=t.T.Enum.optioninletoption=tryleto=List.find(funo->some_ofo.T.Option.name=name)optionsinO.({t=o;obj=None})withNot_found->errorname("unknown enum option: "^U.quotename)inE.({t=t;option=option})andparse_listtxml_elem=debug"parse_list: %s\n"(some_oft.T.Piqi_list.name);letobj_type=some_oft.T.Piqi_list.piqtypeinlet_name,l=xml_eleminletcontents=List.map(parse_list_itemobj_type)linL.({t=t;obj=contents})andparse_list_itemobj_typexml=debug"parse_list_item\n";matchxmlwith|`Elem(("item",l)asxml_elem)->parse_objobj_typexml_elem|_->errorxml"<item> XML element expected as a list item value"(* XXX: roll-up multiple enclosed aliases into one? *)andparse_aliastx=letopenT.Aliasinletobj_type=some_oft.piqtypeindebug"parse_alias: %s\n"(some_oft.T.Alias.name);letobj=parse_objobj_typexinA.({t=t;obj=obj})let_=Piqobj.of_xml:=parse_obj(* parse top-level Piq object formatted as XML *)letparse_objtxml=(* NOTE: we don't bother checking the name of the root element -- it doesn't
* have any meaning anyway *)matchxmlwith|`Elemxml_elem->parse_objtxml_elem|_->errorxml"XML root element expected"