Changeset 1537 for smart/trunk
- Timestamp:
- Sep 26, 2012, 12:26:15 PM (13 years ago)
- File:
-
- 1 edited
-
smart/trunk/p/C0SXPATH.m (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
smart/trunk/p/C0SXPATH.m
r1526 r1537 1 C0SXPATH ; CCDCCR/GPL - XPATH XML manipulation utilities; 6/1/08 2 ;;1.0;C0S;;May 19, 2009;Build 23 ;Copyright 2008-2012 George Lilly. Licensed under the terms of the GNU4 ;General Public License See attached copy of the License.5 ;6 ;This program is free software; you can redistribute it and/or modify7 ;it under the terms of the GNU General Public License as published by8 ;the Free Software Foundation; either version 2 of the License, or9 ;(at your option) any later version.10 ;11 ;This program is distributed in the hope that it will be useful,12 ;but WITHOUT ANY WARRANTY; without even the implied warranty of13 ;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the14 ;GNU General Public License for more details.15 ;16 ;You should have received a copy of the GNU General Public License along17 ;with this program; if not, write to the Free Software Foundation, Inc.,18 ;51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.19 ;20 W "This is an XML XPATH utility library",!21 W !22 Q23 ;24 OUTPUT(OUTARY,OUTNAME,OUTDIR) ; WRITE AN ARRAY TO A FILE25 ;26 N Y27 S Y=$$GTF^%ZISH(OUTARY,$QL(OUTARY),OUTDIR,OUTNAME)28 I Y Q 1_U_"WROTE FILE: "_OUTNAME_" TO "_OUTDIR29 I 'Y Q 0_U_"ERROR WRITING FILE"_OUTNAME_" TO "_OUTDIR30 Q31 ;32 PUSH(STK,VAL) ; pushs VAL onto STK and updates STK(0)33 ; VAL IS A STRING AND STK IS PASSED BY NAME34 ;35 I '$D(@STK@(0)) S @STK@(0)=0 ; IF THE ARRAY IS EMPTY, INITIALIZE36 S @STK@(0)=@STK@(0)+1 ; INCREMENT ARRAY DEPTH37 S @STK@(@STK@(0))=VAL ; PUT VAL A THE END OF THE ARRAY38 Q39 ;40 POP(STK,VAL) ; POPS THE LAST VALUE OFF THE STK AND RETURNS IT IN VAL41 ; VAL AND STK ARE PASSED BY REFERENCE42 ;43 I @STK@(0)<1 D ; IF ARRAY IS EMPTY44 . S VAL=""45 . S @STK@(0)=046 I @STK@(0)>0 D ;47 . S VAL=@STK@(@STK@(0))48 . K @STK@(@STK@(0))49 . S @STK@(0)=@STK@(0)-1 ; NEW DEPTH OF THE ARRAY50 Q51 ;52 PUSHA(ADEST,ASRC) ; PUSH ASRC ONTO ADEST, BOTH PASSED BY NAME53 ;54 N ZGI55 F ZGI=1:1:@ASRC@(0) D ; FOR ALL OF THE SOURCE ARRAY56 . D PUSH(ADEST,@ASRC@(ZGI)) ; PUSH ONE ELEMENT57 Q58 ;59 MKMDX(STK,RTN,INREDUX) ; MAKES A MUMPS INDEX FROM THE ARRAY STK60 ; RTN IS SET TO //FIRST/SECOND/THIRD FOR THREE ARRAY ELEMENTS61 ; REDUX IS A STRING TO REMOVE FROM THE RESULT62 S RTN=""63 N I64 ; W "STK= ",STK,!65 I @STK@(0)>0 D ; IF THE ARRAY IS NOT EMPTY66 . S RTN="//"_@STK@(1) ; FIRST ELEMENT NEEDS NO SEMICOLON67 . I @STK@(0)>1 D ; SUBSEQUENT ELEMENTS NEED A SEMICOLON68 . . F I=2:1:@STK@(0) S RTN=RTN_"/"_@STK@(I)69 I $G(INREDUX)'="" S RTN=$P(RTN,INREDUX,1)_$P(RTN,INREDUX,2)70 Q71 ;72 XNAME(ISTR) ; FUNCTION TO EXTRACT A NAME FROM AN XML FRAG73 ; </NAME> AND <NAME ID=XNAME> WILL RETURN NAME74 ; ISTR IS PASSED BY VALUE75 N CUR,TMP76 I ISTR?.E1"<".E D ; STRIP OFF LEFT BRACKET77 . S TMP=$P(ISTR,"<",2)78 I TMP?1"/".E D ; ALSO STRIP OFF SLASH IF PRESENT IE </NAME>79 . S TMP=$P(TMP,"/",2)80 S CUR=$P(TMP,">",1) ; EXTRACT THE NAME81 ; W "CUR= ",CUR,!82 I CUR?.1"_"1.A1" ".E D ; CONTAINS A BLANK IE NAME ID=TEST>83 . S CUR=$P(CUR," ",1) ; STRIP OUT BLANK AND AFTER84 ; W "CUR2= ",CUR,!85 Q CUR86 ;87 XVAL(ISTR) ; EXTRACTS THE VALUE FROM A FRAGMENT OF XML88 ; <NAME>VALUE</NAME> WILL RETURN VALUE89 N G90 S G=$P(ISTR,">",2) ;STRIP OFF <NAME>91 Q $P(G,"<",1) ; STRIP OFF </NAME> LEAVING VALUE92 ;93 VDX2VDV(OUTVDV,INVDX) ; CONVERT AN VDX ARRAY TO VDV94 ; VDX: @INVDX@(XPATH)=VALUE95 ; VDV: @OUTVDV@(X1X2X3X4)=VALUE96 ; THE VDV DATANAMES MIGHT BE MORE CONVENIENT FOR USE IN CODE97 ; AN INDEX IS PROVIDED TO GO BACK TO VDX FOR CONVERSIONS98 ; @VDV@("XPATH",X1X2X3X4)="XPATH"99 N ZA,ZI,ZW100 S ZI=""101 F S ZI=$O(@INVDX@(ZI)) Q:ZI="" D ;102 . S ZW=$TR(ZI,"/","") ; ELIMINATE ALL SLASHES - CAMEL CASE VARIABLE NAME103 . W ZW,!104 . S @OUTVDV@(ZW)=@INVDX@(ZI)105 . S @OUTVDV@("XPATH",ZW)=ZI106 Q107 ;108 VDX2XPG(OUTXPG,INVDX) ; CONVERT AN VDX ARRAY TO XPG109 ; VDX: @VDX@(XPATH)=VALUE110 ; XPG: @(VDX(X1,X2,X3,X4))@=VALUE111 ; THIS IS A STEP TOWARD GENERATING XML FROM A VDX112 N ZA,ZI,ZW113 S ZI=""114 F S ZI=$O(@INVDX@(ZI)) Q:ZI="" D ;115 . S ZW=$E(ZI,3,$L(ZI)) ; STRIP OFF INITIAL //116 . S ZW2=$P(ZW,"/",1)117 . F ZK=1:1:$L(ZW,"/") D PUSH("ZA",$P(ZW,"/",ZK))118 . ;ZWR ZA119 . S ZW2=ZA(1)120 . F ZK=2:1:ZA(0) D ;121 . . S ZW2=ZW2_""","""_ZA(ZK)122 . K ZA123 . S ZW2=""""_ZW2_""""124 . W ZW2,!125 . S ZN=OUTXPG_"("_ZW2_")"126 . S @ZN=@INVDX@(ZI)127 Q128 ;129 XML2XPG(OUTXPG,INXML) ; CONVERT AN XML ARRAY, PASSED BY NAME TO AN XPG ARRAY130 ; XPG MEANS XPATH GLOBAL AND HAS THE FORM @OUTXPG@("X1","X2","X3")=VALUE131 ;132 ;N G1133 D INDEX(INXML,"G1",1) ; PRODUCES A VDX ARRAY IN G1, NO INDEX IS PRODUCED134 D VDX2XPG(OUTXPG,"G1") ; CONVERTS THE VDX ARRAY TO XPG FORM135 Q136 ;137 DO 138 D XPG2XML("^GPL2B","^GPL2A")139 Q140 ;141 T1 ; TEST OUT THESE ROUTINES142 D XML2XPG("G2","^GPL")143 D XPG2XML("G3","G2")144 K ^GPLOUT145 M ^GPLOUT=G3146 W $$OUTPUT^C0CXPATH("^GPLOUT(1)","GPLTEST.xml","/home/vademo2/EHR/p")147 Q148 ;149 XPG2XML(OUTXML,INXPG) ;150 N C0CN,FWD,ZA,G,GA,ZQ151 S ZQ=0 ; QUIT FLAG152 F Q:ZQ=1 D ; LOOP THROUGH EVERYTHING153 . I '$D(C0CN) D ; FIRST TIME THROUGH154 . . K @OUTXML ; MAKE SURE OUTPUT ARRAY IS CLEAR155 . . S FWD=1 ; START OUT GOING FORWARD THROUGH SUBSCRIPTS156 . . S G=$Q(@INXPG) ; THIS ONE157 . . S GN=$Q(@G) ; NEXT ONE158 . . S C0CN=1 ; SUBSCRIPT COUNT159 . . S ZQ=0 ; QUIT FLAG160 . . D ZXO("?xml version=""1.0"" encoding=""UTF-8""?") ;MAKE IT REAL XML161 . . I $QS(G,1)="ContinuityOfCareRecord" D ;162 . . . D ZXO("?xml-stylesheet type=""text/xsl"" href=""ccr.xsl""?") ; HACK TO MAKE THE CCR STYLESHEET WORK163 . I FWD D ; GOING FORWARDS164 . . I C0CN<$QL(G) D ; NOT A DATA NODE165 . . . S ZA=$QS(G,C0CN) ; PULL OUT THE SUBSCRIPT166 . . . D ZXO(ZA) ; AND OPEN AN XML ELEMENT167 . . . I @OUTXML@(@OUTXML@(0))="<ContinuityOfCareRecord>" D ;168 . . . . S @OUTXML@(@OUTXML@(0))="<ContinuityOfCareRecord xmlns=""urn:astm-org:CCR"">"169 . . . S C0CN=C0CN+1 ; MOVE TO THE NEXT ONE170 . . E D ; AT THE DATA NODE171 . . . S ZA=$QS(G,C0CN) ; PULL OUT THE SUBSCRIPT172 . . . D ZXVAL(ZA,@G) ; OUTPUT <X>VAL</X> FOR DATA NODE173 . . . S FWD=0 ; GO BACKWARDS174 . I 'FWD D ;GOING BACKWARDS175 . . S GN=$Q(@G) ;NEXT XPATH176 . . ;W "NEXT!",GN,!177 . . S C0CN=C0CN-1 ; PREVIOUS SUBSCRIPT178 . . I GN'="" D ;179 . . . I $QS(G,C0CN)'=$QS(GN,C0CN) D ; NEED TO CLOSE OFF ELEMENT180 . . . . D ZXC($QS(G,C0CN)) ;181 . . . E I GN'="" D ; MORE ELEMENTS AT THIS LEVEL182 . . . . S G=$Q(@G) ; ADVANCE TO NEW XPATH183 . . . . S C0CN=C0CN+1 ; GET READY TO PROCESS NEXT SUBSCRIPT184 . . . . S FWD=1 ; GOING FORWARD NOW185 . I (GN="")&(C0CN=1) D Q ; WHEN WE ARE ALL DONE186 . . D ZXC($QS(G,C0CN)) ; LAST ONE187 . . S ZQ=1 ; QUIT NOW188 Q189 ;190 ZXO(WHAT) 191 D PUSH("GA",WHAT)192 D PUSH(OUTXML,"<"_WHAT_">")193 Q194 ;195 ZXC(WHAT) 196 D POP("GA",.TMP)197 D PUSH(OUTXML,"</"_WHAT_">")198 Q199 ;200 ZXVAL(WHAT,VAL) 201 D PUSH(OUTXML,"<"_WHAT_">"_VAL_"</"_WHAT_">")202 Q203 ;204 INDEX(IZXML,VDX,NOINX,TEMPLATE,REDUX) ; parse XML in IZXML and produce205 ; an XPATH index; REDUX is a string to be removed from each xpath206 ; GPL 7/14/09 OPTIONALLY GENERATE AN XML TEMPLATE IF PASSED BY NAME207 ; TEMPLATE IS IDENTICAL TO THE PARSED XML LINE BY LINE208 ; EXCEPT THAT DATA VALUES ARE REPLACED WITH @@XPATH@@ FOR THE XPATH OF THE TAG209 ; GPL 5/24/09 AND OPTIONALLY PRODUCE THE VDX ARRAY PASSED BY NAME210 ; @VDX@("XPATH")=VALUE211 ; ex. @IZXML@("XPATH")=FIRSTLINE^LASTLINE212 ; WHERE FIRSTLINE AND LASTLINE ARE THE BEGINNING AND ENDING OF THE213 ; XML SECTION214 ; IZXML IS PASSED BY NAME215 ; IF NOINX IS SET TO 1, NO INDEX WILL BE GENERATED, BUT THE VDX WILL BE216 N I,LINE,FIRST,LAST,CUR,TMP,MDX,FOUND,CURVAL,DVDX,LCNT217 N C0CSTK ; LEAVE OUT FOR DEBUGGING218 I '$D(REDUX) S REDUX=""219 I '$D(NOINX) S NOINX=0 ; IF NOT PASSED, GENERATE AN INDEX220 N ZXML221 I NOINX S ZXML=$NA(^TMP("C0CINDEX",$J)) ; TEMP PLACE FOR INDEX TO DISCARD222 E S ZXML=IZXML ; PLACE FOR INDEX TO KEEP223 I '$D(@IZXML@(0)) D ; IF COUNT NOT IN NODE 0 COUNT THEM224 . S I="",LCNT=0225 . F S I=$O(@IZXML@(I)) Q:I="" S LCNT=LCNT+1226 E S LCNT=@IZXML@(0) ; LINE COUNT PASSED IN ARRAY227 I LCNT=0 D Q ; NO XML PASSED228 . W "ERROR IN XML FILE",!229 S DVDX=0 ; DEFAULT DO NOT PRODUCE VDX INDEX230 I $D(VDX) S DVDX=1 ; IF NAME PASSED, DO VDX231 S C0CSTK(0)=0 ; INITIALIZE STACK232 K LKASD ; KILL LOOKASIDE ARRAY233 D MKLASD(.LKASD,IZXML) ;MAKE LOOK ASIDE BUFFER FOR MULTIPLES234 F I=1:1:LCNT D ; PROCESS THE ENTIRE ARRAY235 . S LINE=@IZXML@(I)236 . I $D(TEMPLATE) D ;IF TEMPLATE IS REQUESTED237 . . S @TEMPLATE@(I)=$$CLEAN(LINE)238 . ;W LINE,!239 . S FOUND=0 ; INTIALIZED FOUND FLAG240 . I LINE?.E1"<!".E S FOUND=1 ; SKIP OVER COMMENTS241 . I FOUND'=1 D242 . . I (LINE?.E1"<"1.E1"</".E)!(LINE?.E1"<"1.E1"/>".E) D243 . . . ; THIS IS THE CASE THERE SECTION BEGINS AND ENDS244 . . . ; ON THE SAME LINE245 . . . ; W "FOUND ",LINE,!246 . . . S FOUND=1 ; SET FOUND FLAG247 . . . S CUR=$$XNAME(LINE) ; EXTRACT THE NAME248 . . . S CUR=CUR_$G(LKASD(CUR,I)) ; HANDLE MULTIPLES249 . . . D PUSH("C0CSTK",CUR) ; ADD TO THE STACK250 . . . D MKMDX("C0CSTK",.MDX,REDUX) ; GENERATE THE M INDEX251 . . . ; W "MDX=",MDX,!252 . . . I $D(@ZXML@(MDX)) D ; IN THE INDEX, IS A MULTIPLE253 . . . . ;I '$D(ZDUP(MDX)) S ZDUP(MDX)=2254 . . . . ;E S ZDUP(MDX)=ZDUP(MDX)+1255 . . . . ;W "DUP:",MDX,!256 . . . . ;I '$D(CURVAL) S CURVAL=""257 . . . . ;I DVDX S @VDX@(MDX_"["_ZDUP(MDX)_"]")=CURVAL258 . . . . S $P(@ZXML@(MDX),"^",2)=I ; UPDATE LAST LINE NUMBER259 . . . I '$D(@ZXML@(MDX)) D ; NOT IN THE INDEX, NOT A MULTIPLE260 . . . . S @ZXML@(MDX)=I_"^"_I ; ADD INDEX ENTRY-FIRST AND LAST261 . . . . S CURVAL=$$XVAL(LINE) ; VALUE262 . . . . S $P(@ZXML@(MDX),"^",3)=CURVAL ; THIRD PIECE263 . . . . I DVDX S @VDX@(MDX)=CURVAL ; FILL IN VDX ARRAY IF REQUESTED264 . . . . I $D(TEMPLATE) D ; IF TEMPLATE IS REQUESTED265 . . . . . S LINE=$$CLEAN(LINE) ; CLEAN OUT CONTROL CHARACTERS266 . . . . . S @TEMPLATE@(I)=$P(LINE,">",1)_">@@"_MDX_"@@</"_$P(LINE,"</",2)267 . . . D POP("C0CSTK",.TMP) ; REMOVE FROM STACK268 . I FOUND'=1 D ; THE LINE DOESN'T CONTAIN THE START AND END269 . . I LINE?.E1"</"1.E D ; LINE CONTAINS END OF A SECTION270 . . . ; W "FOUND ",LINE,!271 . . . S FOUND=1 ; SET FOUND FLAG272 . . . S CUR=$$XNAME(LINE) ; EXTRACT THE NAME273 . . . D MKMDX("C0CSTK",.MDX) ; GENERATE THE M INDEX274 . . . S $P(@ZXML@(MDX),"^",2)=I ; UPDATE LAST LINE NUMBER275 . . . D POP("C0CSTK",.TMP) ; REMOVE FROM STACK276 . . . S TMP=$P(TMP,"[",1) ; REMOVE [X] FROM MULTIPLE277 . . . I TMP'=CUR D ; MALFORMED XML, END MUST MATCH START278 . . . . W "MALFORMED XML ",CUR,"LINE "_I_LINE,!279 . . . . D PARY("C0CSTK") ; PRINT OUT THE STACK FOR DEBUGING280 . . . . Q281 . I FOUND'=1 D ; THE LINE MIGHT CONTAIN A SECTION BEGINNING282 . . I (LINE?.E1"<"1.E)&(LINE'["?>") D ; BEGINNING OF A SECTION283 . . . ; W "FOUND ",LINE,!284 . . . S FOUND=1 ; SET FOUND FLAG285 . . . S CUR=$$XNAME(LINE) ; EXTRACT THE NAME286 . . . S CUR=CUR_$G(LKASD(CUR,I)) ; HANDLE MULTIPLES287 . . . D PUSH("C0CSTK",CUR) ; ADD TO THE STACK288 . . . D MKMDX("C0CSTK",.MDX) ; GENERATE THE M INDEX289 . . . ; W "MDX=",MDX,!290 . . . I $D(@ZXML@(MDX)) D ; IN THE INDEX, IS A MULTIPLE291 . . . . S $P(@ZXML@(MDX),"^",2)=I ; UPDATE LAST LINE NUMBER292 . . . . ;B293 . . . I '$D(@ZXML@(MDX)) D ; NOT IN THE INDEX, NOT A MULTIPLE294 . . . . S @ZXML@(MDX)=I_"^" ; INSERT INTO THE INDEX295 S @ZXML@("INDEXED")=""296 S @ZXML@("//")="1^"_LCNT ; ROOT XPATH297 I NOINX K @ZXML ; DELETE UNWANTED INDEX298 Q299 ;300 MKLASD(OUTBUF,INARY) ; CREATE A LOOKASIDE BUFFER FOR MULTILPLES301 ;302 N ZI,ZN,ZA,ZLINE,ZLINE2,CUR,CUR2303 F ZI=1:1:LCNT-1 D ; PROCESS THE ENTIRE ARRAY304 . S ZLINE=@IZXML@(ZI)305 . I ZI<LCNT S ZLINE2=@IZXML@(ZI+1)306 . I ZLINE?.E1"</"1.E D ; NEXT LINE CONTAINS END OF A SECTION307 . . S CUR=$$XNAME(ZLINE) ; EXTRACT THE NAME308 . . I (ZLINE2?.E1"<"1.E)&(ZLINE'["?>") D ; BEGINNING OF A SECTION309 . . . S CUR2=$$XNAME(ZLINE2) ; EXTRACT THE NAME310 . . . I CUR=CUR2 D ; IF THIS IS A MULTIPLE311 . . . . S OUTBUF(CUR,ZI+1)=""312 ;ZWR OUTBUF313 S ZI=""314 F S ZI=$O(OUTBUF(ZI)) Q:ZI="" D ; FOR EACH KIND OF MULTIPLE315 . S ZN=$O(OUTBUF(ZI,"")) ; LINE NUMBER OF SECOND MULTIPLE316 . F S ZN=$O(@IZXML@(ZN),-1) Q:ZN="" I $E($P(@IZXML@(ZN),"<"_ZI,2),1,1)=">" Q ;317 . S OUTBUF(ZI,ZN)=""318 S ZA=1,ZI="",ZN=""319 F S ZI=$O(OUTBUF(ZI)) Q:ZI="" D ; ADDING THE COUNT FOR THE MULIPLES [x]320 . S ZN="",ZA=1321 . F S ZN=$O(OUTBUF(ZI,ZN)) Q:ZN="" D ;322 . . S OUTBUF(ZI,ZN)="["_ZA_"]"323 . . S ZA=ZA+1324 Q325 ;326 CLEAN(STR,TR) ; extrinsic function; returns string327 ;; Removes all non printable characters from a string.328 ;; STR by Value329 ;; TR IS OPTIONAL TO IMPROVE PERFORMANCE330 N TR,I331 I '$D(TR) D ;332 . F I=0:1:31 S TR=$G(TR)_$C(I)333 . S TR=TR_$C(127)334 QUIT $TR(STR,TR)335 ;336 QUERY(IARY,XPATH,OARY) ; RETURNS THE XML ARRAY MATCHING THE XPATH EXPRESSION337 ; XPATH IS OF THE FORM "//FIRST/SECOND/THIRD"338 ; IARY AND OARY ARE PASSED BY NAME339 I '$D(@IARY@("INDEXED")) D ; INDEX IS NOT PRESENT IN IARY340 . D INDEX(IARY) ; GENERATE AN INDEX FOR THE XML341 N FIRST,LAST ; FIRST AND LAST LINES OF ARRAY TO RETURN342 N TMP,I,J,QXPATH343 S FIRST=1344 I '$D(@IARY@(0)) D ; LINE COUNT NOT IN ZERO NODE345 . S @IARY@(0)=$O(@IARY@("//"),-1) ; THIS SHOULD USUALLY WORK346 S LAST=@IARY@(0) ; FIRST AND LAST DEFAULT TO ROOT347 I XPATH'="//" D ; NOT A ROOT QUERY348 . S TMP=@IARY@(XPATH) ; LOOK UP LINE VALUES349 . S FIRST=$P(TMP,"^",1)350 . S LAST=$P(TMP,"^",2)351 K @OARY352 S @OARY@(0)=+LAST-FIRST+1353 S J=1354 FOR I=FIRST:1:LAST D355 . S @OARY@(J)=@IARY@(I) ; COPY THE LINE TO OARY356 . S J=J+1357 ; ZWR OARY358 Q359 ;360 XF(IDX,XPATH) ; EXTRINSIC TO RETURN THE STARTING LINE FROM AN XPATH361 ; INDEX WITH TWO PIECES START^FINISH362 ; IDX IS PASSED BY NAME363 Q $P(@IDX@(XPATH),"^",1)364 ;365 XL(IDX,XPATH) ; EXTRINSIC TO RETURN THE LAST LINE FROM AN XPATH366 ; INDEX WITH TWO PIECES START^FINISH367 ; IDX IS PASSED BY NAME368 Q $P(@IDX@(XPATH),"^",2)369 ;370 START(ISTR) ; EXTRINSIC TO RETURN THE STARTING LINE FROM AN INDEX371 ; TYPE STRING WITH THREE PIECES ARRAY;START;FINISH372 ; COMPANION TO FINISH ; IDX IS PASSED BY NAME373 Q $P(ISTR,";",2)374 ;375 FINISH(ISTR) ; EXTRINSIC TO RETURN THE LAST LINE FROM AN INDEX376 ; TYPE STRING WITH THREE PIECES ARRAY;START;FINISH377 Q $P(ISTR,";",3)378 ;379 ARRAY(ISTR) ; EXTRINSIC TO RETURN THE ARRAY REFERENCE FROM AN INDEX380 ; TYPE STRING WITH THREE PIECES ARRAY;START;FINISH381 Q $P(ISTR,";",1)382 ;383 BUILD(BLIST,BDEST) ; A COPY MACHINE THAT TAKE INSTRUCTIONS IN ARRAY BLIST384 ; WHICH HAVE ARRAY;START;FINISH AND COPIES THEM TO DEST385 ; DEST IS CLEARED TO START386 ; USES PUSH TO DO THE COPY387 N I388 K @BDEST389 F I=1:1:@BLIST@(0) D ; FOR EACH INSTRUCTION IN BLIST390 . N J,ATMP391 . S ATMP=$$ARRAY(@BLIST@(I))392 . I $G(DEBUG) W "ATMP=",ATMP,!393 . I $G(DEBUG) W @BLIST@(I),!394 . F J=$$START(@BLIST@(I)):1:$$FINISH(@BLIST@(I)) D ;395 . . ; FOR EACH LINE IN THIS INSTR396 . . I $G(DEBUG) W "BDEST= ",BDEST,!397 . . I $G(DEBUG) W "ATMP= ",@ATMP@(J),!398 . . D PUSH(BDEST,@ATMP@(J))399 Q400 ;401 QUEUE(BLST,ARRAY,FIRST,LAST) ; ADD AN ENTRY TO A BLIST402 ;403 I $G(DEBUG) W "QUEUEING ",BLST,!404 D PUSH(BLST,ARRAY_";"_FIRST_";"_LAST)405 Q406 ;407 CP(CPSRC,CPDEST) ; COPIES CPSRC TO CPDEST BOTH PASSED BY NAME408 ; KILLS CPDEST FIRST409 N CPINSTR410 I $G(DEBUG) W "MADE IT TO COPY",CPSRC,CPDEST,!411 I @CPSRC@(0)<1 D ; BAD LENGTH412 . W "ERROR IN COPY BAD SOURCE LENGTH: ",CPSRC,!413 . Q414 ; I '$D(@CPDEST@(0)) S @CPDEST@(0)=0 ; IF THE DEST IS EMPTY, INIT415 D QUEUE("CPINSTR",CPSRC,1,@CPSRC@(0)) ; BLIST FOR ENTIRE ARRAY416 D BUILD("CPINSTR",CPDEST)417 Q418 ;419 QOPEN(QOBLIST,QOXML,QOXPATH) ; ADD ALL BUT THE LAST LINE OF QOXML TO QOBLIST420 ; WARNING NEED TO DO QCLOSE FOR SAME XML BEFORE CALLING BUILD421 ; QOXPATH IS OPTIONAL - WILL OPEN INSIDE THE XPATH POINT422 ; USED TO INSERT CHILDREN NODES423 I @QOXML@(0)<1 D ; MALFORMED XML424 . W "MALFORMED XML PASSED TO QOPEN: ",QOXML,!425 . Q426 I $G(DEBUG) W "DOING QOPEN",!427 N S1,E1,QOT,QOTMP428 S S1=1 ; OPEN FROM THE BEGINNING OF THE XML429 I $D(QOXPATH) D ; XPATH PROVIDED430 . D QUERY(QOXML,QOXPATH,"QOT") ; INSURE INDEX431 . S E1=$P(@QOXML@(QOXPATH),"^",2)-1432 I '$D(QOXPATH) D ; NO XPATH PROVIDED, OPEN AT ROOT433 . S E1=@QOXML@(0)-1434 D QUEUE(QOBLIST,QOXML,S1,E1)435 ; S QOTMP=QOXML_"^"_S1_"^"_E1436 ; D PUSH(QOBLIST,QOTMP)437 Q438 ;439 QCLOSE(QCBLIST,QCXML,QCXPATH) ; CLOSE XML AFTER A QOPEN440 ; ADDS THE LIST LINE OF QCXML TO QCBLIST441 ; USED TO FINISH INSERTING CHILDERN NODES442 ; QCXPATH IS OPTIONAL - IF PROVIDED, WILL CLOSE UNTIL THE END443 ; IF QOPEN WAS CALLED WITH XPATH, QCLOSE SHOULD BE TOO444 I @QCXML@(0)<1 D ; MALFORMED XML445 . W "MALFORMED XML PASSED TO QCLOSE: ",QCXML,!446 I $G(DEBUG) W "GOING TO CLOSE",!447 N S1,E1,QCT,QCTMP448 S E1=@QCXML@(0) ; CLOSE UNTIL THE END OF THE XML449 I $D(QCXPATH) D ; XPATH PROVIDED450 . D QUERY(QCXML,QCXPATH,"QCT") ; INSURE INDEX451 . S S1=$P(@QCXML@(QCXPATH),"^",2) ; REMAINING XML452 I '$D(QCXPATH) D ; NO XPATH PROVIDED, CLOSE AT ROOT453 . S S1=@QCXML@(0)454 D QUEUE(QCBLIST,QCXML,S1,E1)455 ; D PUSH(QCBLIST,QCXML_";"_S1_";"_E1)456 Q457 ;458 INSERT(INSXML,INSNEW,INSXPATH) ; INSERT INSNEW INTO INSXML AT THE459 ; INSXPATH XPATH POINT INSXPATH IS OPTIONAL - IF IT IS460 ; OMITTED, INSERTION WILL BE AT THE ROOT461 ; NOTE INSERT IS NON DESTRUCTIVE AND WILL ADD THE NEW462 ; XML AT THE END OF THE XPATH POINT463 ; INSXML AND INSNEW ARE PASSED BY NAME INSXPATH IS A VALUE464 N INSBLD,INSTMP465 I $G(DEBUG) W "DOING INSERT ",INSXML,INSNEW,INSXPATH,!466 I $G(DEBUG) F G1=1:1:@INSXML@(0) W @INSXML@(G1),!467 I '$D(@INSXML@(1)) D ; INSERT INTO AN EMPTY ARRAY468 . D CP^C0CXPATH(INSNEW,INSXML) ; JUST COPY INTO THE OUTPUT469 I $D(@INSXML@(1)) D ; IF ORIGINAL ARRAY IS NOT EMPTY470 . I '$D(@INSXML@(0)) S @INSXML@(0)=$O(@INSXML@(""),-1) ;SET LENGTH471 . I $D(INSXPATH) D ; XPATH PROVIDED472 . . D QOPEN("INSBLD",INSXML,INSXPATH) ; COPY THE BEFORE473 . . I $G(DEBUG) D PARY^C0CXPATH("INSBLD")474 . I '$D(INSXPATH) D ; NO XPATH PROVIDED, OPEN AT ROOT475 . . D QOPEN("INSBLD",INSXML,"//") ; OPEN WITH ROOT XPATH476 . I '$D(@INSNEW@(0)) S @INSNEW@(0)=$O(@INSNEW@(""),-1) ;SIZE OF XML477 . D QUEUE("INSBLD",INSNEW,1,@INSNEW@(0)) ; COPY IN NEW XML478 . I $D(INSXPATH) D ; XPATH PROVIDED479 . . D QCLOSE("INSBLD",INSXML,INSXPATH) ; CLOSE WITH XPATH480 . I '$D(INSXPATH) D ; NO XPATH PROVIDED, CLOSE AT ROOT481 . . D QCLOSE("INSBLD",INSXML,"//") ; CLOSE WITH ROOT XPATH482 . D BUILD("INSBLD","INSTMP") ; PUT RESULTS IN INDEST483 . D CP^C0CXPATH("INSTMP",INSXML) ; COPY BUFFER TO SOURCE484 Q485 ;486 INSINNER(INNXML,INNNEW,INNXPATH) ; INSERT THE INNER XML OF INNNEW487 ; INTO INNXML AT THE INNXPATH XPATH POINT488 ;489 N INNBLD,UXPATH490 N INNTBUF491 S INNTBUF=$NA(^TMP($J,"INNTBUF"))492 I '$D(INNXPATH) D ; XPATH NOT PASSED493 . S UXPATH="//" ; USE ROOT XPATH494 I $D(INNXPATH) S UXPATH=INNXPATH ; USE THE XPATH THAT'S PASSED495 I '$D(@INNXML@(0)) D ; INNXML IS EMPTY496 . D QUEUE^C0CXPATH("INNBLD",INNNEW,2,@INNNEW@(0)-1) ; JUST INNER497 . D BUILD("INNBLD",INNXML)498 I @INNXML@(0)>0 D ; NOT EMPTY499 . D QOPEN("INNBLD",INNXML,UXPATH) ;500 . D QUEUE("INNBLD",INNNEW,2,@INNNEW@(0)-1) ; JUST INNER XML501 . D QCLOSE("INNBLD",INNXML,UXPATH)502 . D BUILD("INNBLD",INNTBUF) ; BUILD TO BUFFER503 . D CP(INNTBUF,INNXML) ; COPY BUFFER TO DEST504 Q505 ;506 INSB4(XDEST,XNEW) ; INSERT XNEW AT THE BEGINNING OF XDEST507 ; BUT XDEST AN XNEW ARE PASSED BY NAME508 N XBLD,XTMP509 D QUEUE("XBLD",XDEST,1,1) ; NEED TO PRESERVE SECTION ROOT510 D QUEUE("XBLD",XNEW,1,@XNEW@(0)) ; ALL OF NEW XML FIRST511 D QUEUE("XBLD",XDEST,2,@XDEST@(0)) ; FOLLOWED BY THE REST OF SECTION512 D BUILD("XBLD","XTMP") ; BUILD THE RESULT513 D CP("XTMP",XDEST) ; COPY TO THE DESTINATION514 I $G(DEBUG) D PARY("XDEST")515 Q516 ;517 REPLACE(REXML,RENEW,REXPATH) ; REPLACE THE XML AT THE XPATH POINT518 ; WITH RENEW - NOTE THIS WILL DELETE WHAT WAS THERE BEFORE519 ; REXML AND RENEW ARE PASSED BY NAME XPATH IS A VALUE520 ; THE DELETED XML IS PUT IN ^TMP($J,"REPLACE_OLD")521 N REBLD,XFIRST,XLAST,OLD,XNODE,RETMP522 S OLD=$NA(^TMP($J,"REPLACE_OLD"))523 D QUERY(REXML,REXPATH,OLD) ; CREATE INDEX, TEST XPATH, MAKE OLD524 S XNODE=@REXML@(REXPATH) ; PULL OUT FIRST AND LAST LINE PTRS525 S XFIRST=$P(XNODE,"^",1)526 S XLAST=$P(XNODE,"^",2)527 I RENEW="" D ; WE ARE DELETING A SECTION, MUST SAVE THE TAG528 . D QUEUE("REBLD",REXML,1,XFIRST) ; THE BEFORE529 . D QUEUE("REBLD",REXML,XLAST,@REXML@(0)) ; THE REST530 I RENEW'="" D ; NEW XML IS NOT NULL531 . D QUEUE("REBLD",REXML,1,XFIRST-1) ; THE BEFORE532 . D QUEUE("REBLD",RENEW,1,@RENEW@(0)) ; THE NEW533 . D QUEUE("REBLD",REXML,XLAST+1,@REXML@(0)) ; THE REST534 I $G(DEBUG) W "REPLACE PREBUILD",!535 I $G(DEBUG) D PARY("REBLD")536 D BUILD("REBLD","RTMP")537 K @REXML ; KILL WHAT WAS THERE538 D CP("RTMP",REXML) ; COPY IN THE RESULT539 Q540 ;541 DELETE(REXML,REXPATH) ; DELETE THE XML AT THE XPATH POINT542 ; REXML IS PASSED BY NAME XPATH IS A VALUE543 N REBLD,XFIRST,XLAST,OLD,XNODE,RETMP544 S OLD=$NA(^TMP($J,"REPLACE_OLD"))545 D QUERY(REXML,REXPATH,OLD) ; CREATE INDEX, TEST XPATH, MAKE OLD546 S XNODE=@REXML@(REXPATH) ; PULL OUT FIRST AND LAST LINE PTRS547 S XFIRST=$P(XNODE,"^",1)548 S XLAST=$P(XNODE,"^",2)549 D QUEUE("REBLD",REXML,1,XFIRST-1) ; THE BEFORE550 D QUEUE("REBLD",REXML,XLAST+1,@REXML@(0)) ; THE REST551 I $G(DEBUG) D PARY("REBLD")552 D BUILD("REBLD","RTMP")553 K @REXML ; KILL WHAT WAS THERE554 D CP("RTMP",REXML) ; COPY IN THE RESULT555 Q556 ;557 MISSING(IXML,OARY) ; SEARTH THROUGH INXLM AND PUT ANY @@X@@ VARS IN OARY558 ; W "Reporting on the missing",!559 ; W OARY560 I '$D(@IXML@(0)) W "MALFORMED XML PASSED TO MISSING",! Q561 N I562 S @OARY@(0)=0 ; INITIALIZED MISSING COUNT563 F I=1:1:@IXML@(0) D ; LOOP THROUGH WHOLE ARRAY564 . I @IXML@(I)?.E1"@@".E D ; MISSING VARIABLE HERE565 . . D PUSH^C0CXPATH(OARY,$P(@IXML@(I),"@@",2)) ; ADD TO OUTARY566 . . Q567 Q568 ;569 MAP(IXML,INARY,OXML) ; SUBSTITUTE MULTIPLE @@X@@ VARS WITH VALUES IN INARY570 ; AND PUT THE RESULTS IN OXML571 N XCNT572 I '$D(DEBUG) S DEBUG=0573 I '$D(IXML) W "MALFORMED XML PASSED TO MAP",! Q574 I '$D(@IXML@(0)) D ; INITIALIZE COUNT575 . S XCNT=$O(@IXML@(""),-1)576 E S XCNT=@IXML@(0) ;COUNT577 I $O(@INARY@(""))="" W "EMPTY ARRAY PASSED TO MAP",! Q578 N I,J,TNAM,TVAL,TSTR579 S @OXML@(0)=XCNT ; TOTAL LINES IN OUTPUT580 F I=1:1:XCNT D ; LOOP THROUGH WHOLE ARRAY581 . S @OXML@(I)=@IXML@(I) ; COPY THE LINE TO OUTPUT582 . I @OXML@(I)?.E1"@@".E D ; IS THERE A VARIABLE HERE?583 . . S TSTR=$P(@IXML@(I),"@@",1) ; INIT TO PART BEFORE VARS584 . . F J=2:2:10 D Q:$P(@IXML@(I),"@@",J+2)="" ; QUIT IF NO MORE VARS585 . . . I DEBUG W "IN MAPPING LOOP: ",TSTR,!586 . . . S TNAM=$P(@OXML@(I),"@@",J) ; EXTRACT THE VARIABLE NAME587 . . . S TVAL="@@"_$P(@IXML@(I),"@@",J)_"@@" ; DEFAULT UNCHANGED588 . . . I $D(@INARY@(TNAM)) D ; IS THE VARIABLE IN THE MAP?589 . . . . I '$D(@INARY@(TNAM,"F")) D ; NOT A SPECIAL FIELD590 . . . . . S TVAL=@INARY@(TNAM) ; PULL OUT MAPPED VALUE591 . . . . E D DOFLD ; PROCESS A FIELD592 . . . S TVAL=$$SYMENC^MXMLUTL(TVAL) ;MAKE SURE THE VALUE IS XML SAFE593 . . . S TSTR=TSTR_TVAL_$P(@IXML@(I),"@@",J+1) ; ADD VAR AND PART AFTER594 . . S @OXML@(I)=TSTR ; COPY LINE WITH MAPPED VALUES595 . . I DEBUG W TSTR596 I DEBUG W "MAPPED",!597 Q598 ;599 DOFLD ; PROCESS A FILEMAN FIELD REFERENCED BY A VARIABLE600 ;601 Q602 ;603 TRIM(THEXML) ; TAKES OUT ALL NULL ELEMENTS604 ; THEXML IS PASSED BY NAME605 N I,J,TMPXML,DEL,FOUND,INTXT606 S FOUND=0607 S INTXT=0608 I $G(DEBUG) W "DELETING EMPTY ELEMENTS",!609 F I=1:1:(@THEXML@(0)-1) D ; LOOP THROUGH ENTIRE ARRAY610 . S J=@THEXML@(I)611 . I J["<text>" D612 . . S INTXT=1 ; IN HTML SECTION, DON'T TRIM613 . . I $G(DEBUG) W "IN HTML SECTION",!614 . N JM,JP,JPX ; JMINUS AND JPLUS615 . S JM=@THEXML@(I-1) ; LINE BEFORE616 . I JM["</text>" S INTXT=0 ; LEFT HTML SECTION,START TRIM617 . S JP=@THEXML@(I+1) ; LINE AFTER618 . I INTXT=0 D ; IF NOT IN AN HTML SECTION619 . . S JPX=$TR(JP,"/","") ; REMOVE THE SLASH620 . . I J=JPX D ; AN EMPTY ELEMENT ON TWO LINES621 . . . I $G(DEBUG) W I,J,JP,!622 . . . S FOUND=1 ; FOUND SOMETHING TO BE DELETED623 . . . S DEL(I)="" ; SET LINE TO DELETE624 . . . S DEL(I+1)="" ; SET NEXT LINE TO DELETE625 . . I J["><" D ; AN EMPTY ELEMENT ON ONE LINE626 . . . I $G(DEBUG) W I,J,!627 . . . S FOUND=1 ; FOUND SOMETHING TO BE DELETED628 . . . S DEL(I)="" ; SET THE EMPTY LINE UP TO BE DELETED629 . . . I JM=JPX D ;630 . . . . I $G(DEBUG) W I,JM_J_JPX,!631 . . . . S DEL(I-1)=""632 . . . . S DEL(I+1)="" ; SET THE SURROUNDING LINES FOR DEL633 ; . I J'["><" D PUSH("TMPXML",J)634 I FOUND D ; NEED TO DELETE THINGS635 . F I=1:1:@THEXML@(0) D ; COPY ARRAY LEAVING OUT DELELTED LINES636 . . I '$D(DEL(I)) D ; IF THE LINE IS NOT DELETED637 . . . D PUSH("TMPXML",@THEXML@(I)) ; COPY TO TMPXML ARRAY638 . D CP("TMPXML",THEXML) ; REPLACE THE XML WITH THE COPY639 Q FOUND640 ;641 UNMARK(XSEC) ; REMOVE MARKUP FROM FIRST AND LAST LINE OF XML642 ; XSEC IS A SECTION PASSED BY NAME643 N XBLD,XTMP644 D QUEUE("XBLD",XSEC,2,@XSEC@(0)-1) ; BUILD LIST FOR INNER XML645 D BUILD("XBLD","XTMP") ; BUILD THE RESULT646 D CP("XTMP",XSEC) ; REPLACE PASSED XML647 Q648 ;649 PARY(GLO,ZN) ;PRINT AN ARRAY650 ; IF ZN=-1 NO LINE NUMBERS651 N I652 F I=1:1:@GLO@(0) D ;653 . I $G(ZN)=-1 W @GLO@(I),!654 . E W I_" "_@GLO@(I),!655 Q656 ;657 H2ARY(IARYRTN,IHASH,IPRE) ; CONVERT IHASH TO RETURN ARRAY658 ; IPRE IS OPTIONAL PREFIX FOR THE ELEMENTS. USED FOR MUPTIPLES 1^"VAR"^VALUE659 I '$D(IPRE) S IPRE=""660 N H2I S H2I=""661 ; W $O(@IHASH@(H2I)),!662 F S H2I=$O(@IHASH@(H2I)) Q:H2I="" D ; FOR EACH ELEMENT OF THE HASH663 . I $QS(H2I,$QL(H2I))="M" D Q ; SPECIAL CASE FOR MULTIPLES664 . . ;W H2I_"^"_@IHASH@(H2I),!665 . . N IH,IHI666 . . S IH=$NA(@IHASH@(H2I)) ;667 . . S IH2A=$O(@IH@("")) ; SKIP OVER MULTIPLE DISCRIPTOR668 . . S IH2=$NA(@IH@(IH2A)) ; PAST THE "M","DIRETIONS" FOR EXAMPLE669 . . S IHI="" ; INDEX INTO "M" MULTIPLES670 . . F S IHI=$O(@IH2@(IHI)) Q:IHI="" D ; FOR EACH SUB-MULTIPLE671 . . . ; W @IH@(IHI)672 . . . S IH3=$NA(@IH2@(IHI))673 . . . ; W "HEY",IH3,!674 . . . D H2ARY(.IARYRTN,IH3,IPRE_";"_IHI) ; RECURSIVE CALL - INDENTED ELEMENTS675 . . ; W IH,!676 . . ; W "C0CZZ",!677 . . ; W $NA(@IHASH@(H2I)),!678 . . Q ;679 . D PUSH(IARYRTN,IPRE_"^"_H2I_"^"_@IHASH@(H2I))680 . ; W @IARYRTN@(0),!681 Q682 ;683 XVARS(XVRTN,XVIXML) ; RETURNS AN ARRAY XVRTN OF ALL UNIQUE VARIABLES684 ; DEFINED IN INPUT XML XVIXML BY @@VAR@@685 ; XVRTN AND XVIXML ARE PASSED BY NAME686 ;687 N XVI,XVTMP,XVT688 F XVI=1:1:@XVIXML@(0) D ; FOR ALL LINES OF THE XML689 . S XVT=@XVIXML@(XVI)690 . I XVT["@@" S XVTMP($P(XVT,"@@",2))=XVI691 D H2ARY(XVRTN,"XVTMP")692 Q693 ;694 DXVARS(DXIN) ;DISPLAY ALL VARIABLES IN A TEMPLATE695 ; IF PARAMETERS ARE NULL, DEFAULTS TO CCR TEMPLATE696 ;697 N DXUSE,DTMP ; DXUSE IS NAME OF VARIABLE, DTMP IS VARIABLE IF NOT SUPPLIED698 I DXIN="CCR" D ; NEED TO GO GET CCR TEMPLATE699 . D LOAD^C0CCCR0("DTMP") ; LOAD CCR TEMPLATE INTO DXTMP700 . S DXUSE="DTMP" ; DXUSE IS NAME701 E I DXIN="CCD" D ; NEED TO GO GET CCD TEMPLATE702 . D LOAD^C0CCCD1("DTMP") ; LOAD CCR TEMPLATE INTO DXTMP703 . S DXUSE="DTMP" ; DXUSE IS NAME704 E S DXUSE=DXIN ; IF PASSED THE TEMPLATE TO USE705 N DVARS ; PUT VARIABLE NAME RESULTS IN ARRAY HERE706 D XVARS("DVARS",DXUSE) ; PULL OUT VARS707 D PARY^C0CXPATH("DVARS") ;AND DISPLAY THEM708 Q709 ;710 TEST ; Run all the test cases711 D TESTALL^C0CUNIT("C0CXPAT0")712 Q713 ;714 ZTEST(WHICH) ; RUN ONE SET OF TESTS715 N ZTMP716 S DEBUG=1717 D ZLOAD^C0CUNIT("ZTMP","C0CXPAT0")718 D ZTEST^C0CUNIT(.ZTMP,WHICH)719 Q720 ;721 TLIST ; LIST THE TESTS722 N ZTMP723 D ZLOAD^C0CUNIT("ZTMP","C0CXPAT0")724 D TLIST^C0CUNIT(.ZTMP)725 Q726 ;1 C0SXPATH ; CCDCCR/GPL - XPATH XML manipulation utilities; 6/1/08 ; 9/26/12 10:45am 2 ;;1.0;C0S;;May 19, 2009;Build 2 3 ;Copyright 2008-2012 George Lilly. Licensed under the terms of the GNU 4 ;General Public License See attached copy of the License. 5 ; 6 ;This program is free software; you can redistribute it and/or modify 7 ;it under the terms of the GNU General Public License as published by 8 ;the Free Software Foundation; either version 2 of the License, or 9 ;(at your option) any later version. 10 ; 11 ;This program is distributed in the hope that it will be useful, 12 ;but WITHOUT ANY WARRANTY; without even the implied warranty of 13 ;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 ;GNU General Public License for more details. 15 ; 16 ;You should have received a copy of the GNU General Public License along 17 ;with this program; if not, write to the Free Software Foundation, Inc., 18 ;51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 ; 20 W "This is an XML XPATH utility library",! 21 W ! 22 Q 23 ; 24 OUTPUT(OUTARY,OUTNAME,OUTDIR) ; WRITE AN ARRAY TO A FILE 25 ; 26 N Y 27 S Y=$$GTF^%ZISH(OUTARY,$QL(OUTARY),OUTDIR,OUTNAME) 28 I Y Q 1_U_"WROTE FILE: "_OUTNAME_" TO "_OUTDIR 29 I 'Y Q 0_U_"ERROR WRITING FILE"_OUTNAME_" TO "_OUTDIR 30 Q 31 ; 32 PUSH(STK,VAL) ; pushs VAL onto STK and updates STK(0) 33 ; VAL IS A STRING AND STK IS PASSED BY NAME 34 ; 35 I '$D(@STK@(0)) S @STK@(0)=0 ; IF THE ARRAY IS EMPTY, INITIALIZE 36 S @STK@(0)=@STK@(0)+1 ; INCREMENT ARRAY DEPTH 37 S @STK@(@STK@(0))=VAL ; PUT VAL A THE END OF THE ARRAY 38 Q 39 ; 40 POP(STK,VAL) ; POPS THE LAST VALUE OFF THE STK AND RETURNS IT IN VAL 41 ; VAL AND STK ARE PASSED BY REFERENCE 42 ; 43 I @STK@(0)<1 D ; IF ARRAY IS EMPTY 44 . S VAL="" 45 . S @STK@(0)=0 46 I @STK@(0)>0 D ; 47 . S VAL=@STK@(@STK@(0)) 48 . K @STK@(@STK@(0)) 49 . S @STK@(0)=@STK@(0)-1 ; NEW DEPTH OF THE ARRAY 50 Q 51 ; 52 PUSHA(ADEST,ASRC) ; PUSH ASRC ONTO ADEST, BOTH PASSED BY NAME 53 ; 54 N ZGI 55 F ZGI=1:1:@ASRC@(0) D ; FOR ALL OF THE SOURCE ARRAY 56 . D PUSH(ADEST,@ASRC@(ZGI)) ; PUSH ONE ELEMENT 57 Q 58 ; 59 MKMDX(STK,RTN,INREDUX) ; MAKES A MUMPS INDEX FROM THE ARRAY STK 60 ; RTN IS SET TO //FIRST/SECOND/THIRD FOR THREE ARRAY ELEMENTS 61 ; REDUX IS A STRING TO REMOVE FROM THE RESULT 62 S RTN="" 63 N I 64 ; W "STK= ",STK,! 65 I @STK@(0)>0 D ; IF THE ARRAY IS NOT EMPTY 66 . S RTN="//"_@STK@(1) ; FIRST ELEMENT NEEDS NO SEMICOLON 67 . I @STK@(0)>1 D ; SUBSEQUENT ELEMENTS NEED A SEMICOLON 68 . . F I=2:1:@STK@(0) S RTN=RTN_"/"_@STK@(I) 69 I $G(INREDUX)'="" S RTN=$P(RTN,INREDUX,1)_$P(RTN,INREDUX,2) 70 Q 71 ; 72 XNAME(ISTR) ; FUNCTION TO EXTRACT A NAME FROM AN XML FRAG 73 ; </NAME> AND <NAME ID=XNAME> WILL RETURN NAME 74 ; ISTR IS PASSED BY VALUE 75 N CUR,TMP 76 I ISTR?.E1"<".E D ; STRIP OFF LEFT BRACKET 77 . S TMP=$P(ISTR,"<",2) 78 I TMP?1"/".E D ; ALSO STRIP OFF SLASH IF PRESENT IE </NAME> 79 . S TMP=$P(TMP,"/",2) 80 S CUR=$P(TMP,">",1) ; EXTRACT THE NAME 81 ; W "CUR= ",CUR,! 82 I CUR?.1"_"1.A1" ".E D ; CONTAINS A BLANK IE NAME ID=TEST> 83 . S CUR=$P(CUR," ",1) ; STRIP OUT BLANK AND AFTER 84 ; W "CUR2= ",CUR,! 85 Q CUR 86 ; 87 XVAL(ISTR) ; EXTRACTS THE VALUE FROM A FRAGMENT OF XML 88 ; <NAME>VALUE</NAME> WILL RETURN VALUE 89 N G 90 S G=$P(ISTR,">",2) ;STRIP OFF <NAME> 91 Q $P(G,"<",1) ; STRIP OFF </NAME> LEAVING VALUE 92 ; 93 VDX2VDV(OUTVDV,INVDX) ; CONVERT AN VDX ARRAY TO VDV 94 ; VDX: @INVDX@(XPATH)=VALUE 95 ; VDV: @OUTVDV@(X1X2X3X4)=VALUE 96 ; THE VDV DATANAMES MIGHT BE MORE CONVENIENT FOR USE IN CODE 97 ; AN INDEX IS PROVIDED TO GO BACK TO VDX FOR CONVERSIONS 98 ; @VDV@("XPATH",X1X2X3X4)="XPATH" 99 N ZA,ZI,ZW 100 S ZI="" 101 F S ZI=$O(@INVDX@(ZI)) Q:ZI="" D ; 102 . S ZW=$TR(ZI,"/","") ; ELIMINATE ALL SLASHES - CAMEL CASE VARIABLE NAME 103 . W ZW,! 104 . S @OUTVDV@(ZW)=@INVDX@(ZI) 105 . S @OUTVDV@("XPATH",ZW)=ZI 106 Q 107 ; 108 VDX2XPG(OUTXPG,INVDX) ; CONVERT AN VDX ARRAY TO XPG 109 ; VDX: @VDX@(XPATH)=VALUE 110 ; XPG: @(VDX(X1,X2,X3,X4))@=VALUE 111 ; THIS IS A STEP TOWARD GENERATING XML FROM A VDX 112 N ZA,ZI,ZW 113 S ZI="" 114 F S ZI=$O(@INVDX@(ZI)) Q:ZI="" D ; 115 . S ZW=$E(ZI,3,$L(ZI)) ; STRIP OFF INITIAL // 116 . S ZW2=$P(ZW,"/",1) 117 . F ZK=1:1:$L(ZW,"/") D PUSH("ZA",$P(ZW,"/",ZK)) 118 . ;ZWR ZA 119 . S ZW2=ZA(1) 120 . F ZK=2:1:ZA(0) D ; 121 . . S ZW2=ZW2_""","""_ZA(ZK) 122 . K ZA 123 . S ZW2=""""_ZW2_"""" 124 . W ZW2,! 125 . S ZN=OUTXPG_"("_ZW2_")" 126 . S @ZN=@INVDX@(ZI) 127 Q 128 ; 129 XML2XPG(OUTXPG,INXML) ; CONVERT AN XML ARRAY, PASSED BY NAME TO AN XPG ARRAY 130 ; XPG MEANS XPATH GLOBAL AND HAS THE FORM @OUTXPG@("X1","X2","X3")=VALUE 131 ; 132 ;N G1 133 D INDEX(INXML,"G1",1) ; PRODUCES A VDX ARRAY IN G1, NO INDEX IS PRODUCED 134 D VDX2XPG(OUTXPG,"G1") ; CONVERTS THE VDX ARRAY TO XPG FORM 135 Q 136 ; 137 DO 138 D XPG2XML("^GPL2B","^GPL2A") 139 Q 140 ; 141 T1 ; TEST OUT THESE ROUTINES 142 D XML2XPG("G2","^GPL") 143 D XPG2XML("G3","G2") 144 K ^GPLOUT 145 M ^GPLOUT=G3 146 W $$OUTPUT^C0CXPATH("^GPLOUT(1)","GPLTEST.xml","/home/vademo2/EHR/p") 147 Q 148 ; 149 XPG2XML(OUTXML,INXPG) ; 150 N C0CN,FWD,ZA,G,GA,ZQ 151 S ZQ=0 ; QUIT FLAG 152 F Q:ZQ=1 D ; LOOP THROUGH EVERYTHING 153 . I '$D(C0CN) D ; FIRST TIME THROUGH 154 . . K @OUTXML ; MAKE SURE OUTPUT ARRAY IS CLEAR 155 . . S FWD=1 ; START OUT GOING FORWARD THROUGH SUBSCRIPTS 156 . . S G=$Q(@INXPG) ; THIS ONE 157 . . S GN=$Q(@G) ; NEXT ONE 158 . . S C0CN=1 ; SUBSCRIPT COUNT 159 . . S ZQ=0 ; QUIT FLAG 160 . . D ZXO("?xml version=""1.0"" encoding=""UTF-8""?") ;MAKE IT REAL XML 161 . . I $QS(G,1)="ContinuityOfCareRecord" D ; 162 . . . D ZXO("?xml-stylesheet type=""text/xsl"" href=""ccr.xsl""?") ; HACK TO MAKE THE CCR STYLESHEET WORK 163 . I FWD D ; GOING FORWARDS 164 . . I C0CN<$QL(G) D ; NOT A DATA NODE 165 . . . S ZA=$QS(G,C0CN) ; PULL OUT THE SUBSCRIPT 166 . . . D ZXO(ZA) ; AND OPEN AN XML ELEMENT 167 . . . I @OUTXML@(@OUTXML@(0))="<ContinuityOfCareRecord>" D ; 168 . . . . S @OUTXML@(@OUTXML@(0))="<ContinuityOfCareRecord xmlns=""urn:astm-org:CCR"">" 169 . . . S C0CN=C0CN+1 ; MOVE TO THE NEXT ONE 170 . . E D ; AT THE DATA NODE 171 . . . S ZA=$QS(G,C0CN) ; PULL OUT THE SUBSCRIPT 172 . . . D ZXVAL(ZA,@G) ; OUTPUT <X>VAL</X> FOR DATA NODE 173 . . . S FWD=0 ; GO BACKWARDS 174 . I 'FWD D ;GOING BACKWARDS 175 . . S GN=$Q(@G) ;NEXT XPATH 176 . . ;W "NEXT!",GN,! 177 . . S C0CN=C0CN-1 ; PREVIOUS SUBSCRIPT 178 . . I GN'="" D ; 179 . . . I $QS(G,C0CN)'=$QS(GN,C0CN) D ; NEED TO CLOSE OFF ELEMENT 180 . . . . D ZXC($QS(G,C0CN)) ; 181 . . . E I GN'="" D ; MORE ELEMENTS AT THIS LEVEL 182 . . . . S G=$Q(@G) ; ADVANCE TO NEW XPATH 183 . . . . S C0CN=C0CN+1 ; GET READY TO PROCESS NEXT SUBSCRIPT 184 . . . . S FWD=1 ; GOING FORWARD NOW 185 . I (GN="")&(C0CN=1) D Q ; WHEN WE ARE ALL DONE 186 . . D ZXC($QS(G,C0CN)) ; LAST ONE 187 . . S ZQ=1 ; QUIT NOW 188 Q 189 ; 190 ZXO(WHAT) 191 D PUSH("GA",WHAT) 192 D PUSH(OUTXML,"<"_WHAT_">") 193 Q 194 ; 195 ZXC(WHAT) 196 D POP("GA",.TMP) 197 D PUSH(OUTXML,"</"_WHAT_">") 198 Q 199 ; 200 ZXVAL(WHAT,VAL) 201 D PUSH(OUTXML,"<"_WHAT_">"_VAL_"</"_WHAT_">") 202 Q 203 ; 204 INDEX(IZXML,VDX,NOINX,TEMPLATE,REDUX) ; parse XML in IZXML and produce 205 ; an XPATH index; REDUX is a string to be removed from each xpath 206 ; GPL 7/14/09 OPTIONALLY GENERATE AN XML TEMPLATE IF PASSED BY NAME 207 ; TEMPLATE IS IDENTICAL TO THE PARSED XML LINE BY LINE 208 ; EXCEPT THAT DATA VALUES ARE REPLACED WITH @@XPATH@@ FOR THE XPATH OF THE TAG 209 ; GPL 5/24/09 AND OPTIONALLY PRODUCE THE VDX ARRAY PASSED BY NAME 210 ; @VDX@("XPATH")=VALUE 211 ; ex. @IZXML@("XPATH")=FIRSTLINE^LASTLINE 212 ; WHERE FIRSTLINE AND LASTLINE ARE THE BEGINNING AND ENDING OF THE 213 ; XML SECTION 214 ; IZXML IS PASSED BY NAME 215 ; IF NOINX IS SET TO 1, NO INDEX WILL BE GENERATED, BUT THE VDX WILL BE 216 N I,LINE,FIRST,LAST,CUR,TMP,MDX,FOUND,CURVAL,DVDX,LCNT 217 N C0CSTK ; LEAVE OUT FOR DEBUGGING 218 I '$D(REDUX) S REDUX="" 219 I '$D(NOINX) S NOINX=0 ; IF NOT PASSED, GENERATE AN INDEX 220 N ZXML 221 I NOINX S ZXML=$NA(^TMP("C0CINDEX",$J)) ; TEMP PLACE FOR INDEX TO DISCARD 222 E S ZXML=IZXML ; PLACE FOR INDEX TO KEEP 223 I '$D(@IZXML@(0)) D ; IF COUNT NOT IN NODE 0 COUNT THEM 224 . S I="",LCNT=0 225 . F S I=$O(@IZXML@(I)) Q:I="" S LCNT=LCNT+1 226 E S LCNT=@IZXML@(0) ; LINE COUNT PASSED IN ARRAY 227 I LCNT=0 D Q ; NO XML PASSED 228 . W "ERROR IN XML FILE",! 229 S DVDX=0 ; DEFAULT DO NOT PRODUCE VDX INDEX 230 I $D(VDX) S DVDX=1 ; IF NAME PASSED, DO VDX 231 S C0CSTK(0)=0 ; INITIALIZE STACK 232 K LKASD ; KILL LOOKASIDE ARRAY 233 D MKLASD(.LKASD,IZXML) ;MAKE LOOK ASIDE BUFFER FOR MULTIPLES 234 F I=1:1:LCNT D ; PROCESS THE ENTIRE ARRAY 235 . S LINE=@IZXML@(I) 236 . I $D(TEMPLATE) D ;IF TEMPLATE IS REQUESTED 237 . . S @TEMPLATE@(I)=$$CLEAN(LINE) 238 . ;W LINE,! 239 . S FOUND=0 ; INTIALIZED FOUND FLAG 240 . I LINE?.E1"<!".E S FOUND=1 ; SKIP OVER COMMENTS 241 . I FOUND'=1 D 242 . . I (LINE?.E1"<"1.E1"</".E)!(LINE?.E1"<"1.E1"/>".E) D 243 . . . ; THIS IS THE CASE THERE SECTION BEGINS AND ENDS 244 . . . ; ON THE SAME LINE 245 . . . ; W "FOUND ",LINE,! 246 . . . S FOUND=1 ; SET FOUND FLAG 247 . . . S CUR=$$XNAME(LINE) ; EXTRACT THE NAME 248 . . . S CUR=CUR_$G(LKASD(CUR,I)) ; HANDLE MULTIPLES 249 . . . D PUSH("C0CSTK",CUR) ; ADD TO THE STACK 250 . . . D MKMDX("C0CSTK",.MDX,REDUX) ; GENERATE THE M INDEX 251 . . . ; W "MDX=",MDX,! 252 . . . I $D(@ZXML@(MDX)) D ; IN THE INDEX, IS A MULTIPLE 253 . . . . ;I '$D(ZDUP(MDX)) S ZDUP(MDX)=2 254 . . . . ;E S ZDUP(MDX)=ZDUP(MDX)+1 255 . . . . ;W "DUP:",MDX,! 256 . . . . ;I '$D(CURVAL) S CURVAL="" 257 . . . . ;I DVDX S @VDX@(MDX_"["_ZDUP(MDX)_"]")=CURVAL 258 . . . . S $P(@ZXML@(MDX),"^",2)=I ; UPDATE LAST LINE NUMBER 259 . . . I '$D(@ZXML@(MDX)) D ; NOT IN THE INDEX, NOT A MULTIPLE 260 . . . . S @ZXML@(MDX)=I_"^"_I ; ADD INDEX ENTRY-FIRST AND LAST 261 . . . . S CURVAL=$$XVAL(LINE) ; VALUE 262 . . . . S $P(@ZXML@(MDX),"^",3)=CURVAL ; THIRD PIECE 263 . . . . I DVDX S @VDX@(MDX)=CURVAL ; FILL IN VDX ARRAY IF REQUESTED 264 . . . . I $D(TEMPLATE) D ; IF TEMPLATE IS REQUESTED 265 . . . . . S LINE=$$CLEAN(LINE) ; CLEAN OUT CONTROL CHARACTERS 266 . . . . . S @TEMPLATE@(I)=$P(LINE,">",1)_">@@"_MDX_"@@</"_$P(LINE,"</",2) 267 . . . D POP("C0CSTK",.TMP) ; REMOVE FROM STACK 268 . I FOUND'=1 D ; THE LINE DOESN'T CONTAIN THE START AND END 269 . . I LINE?.E1"</"1.E D ; LINE CONTAINS END OF A SECTION 270 . . . ; W "FOUND ",LINE,! 271 . . . S FOUND=1 ; SET FOUND FLAG 272 . . . S CUR=$$XNAME(LINE) ; EXTRACT THE NAME 273 . . . D MKMDX("C0CSTK",.MDX) ; GENERATE THE M INDEX 274 . . . S $P(@ZXML@(MDX),"^",2)=I ; UPDATE LAST LINE NUMBER 275 . . . D POP("C0CSTK",.TMP) ; REMOVE FROM STACK 276 . . . S TMP=$P(TMP,"[",1) ; REMOVE [X] FROM MULTIPLE 277 . . . I TMP'=CUR D ; MALFORMED XML, END MUST MATCH START 278 . . . . W "MALFORMED XML ",CUR,"LINE "_I_LINE,! 279 . . . . D PARY("C0CSTK") ; PRINT OUT THE STACK FOR DEBUGING 280 . . . . Q 281 . I FOUND'=1 D ; THE LINE MIGHT CONTAIN A SECTION BEGINNING 282 . . I (LINE?.E1"<"1.E)&(LINE'["?>") D ; BEGINNING OF A SECTION 283 . . . ; W "FOUND ",LINE,! 284 . . . S FOUND=1 ; SET FOUND FLAG 285 . . . S CUR=$$XNAME(LINE) ; EXTRACT THE NAME 286 . . . S CUR=CUR_$G(LKASD(CUR,I)) ; HANDLE MULTIPLES 287 . . . D PUSH("C0CSTK",CUR) ; ADD TO THE STACK 288 . . . D MKMDX("C0CSTK",.MDX) ; GENERATE THE M INDEX 289 . . . ; W "MDX=",MDX,! 290 . . . I $D(@ZXML@(MDX)) D ; IN THE INDEX, IS A MULTIPLE 291 . . . . S $P(@ZXML@(MDX),"^",2)=I ; UPDATE LAST LINE NUMBER 292 . . . . ;B 293 . . . I '$D(@ZXML@(MDX)) D ; NOT IN THE INDEX, NOT A MULTIPLE 294 . . . . S @ZXML@(MDX)=I_"^" ; INSERT INTO THE INDEX 295 S @ZXML@("INDEXED")="" 296 S @ZXML@("//")="1^"_LCNT ; ROOT XPATH 297 I NOINX K @ZXML ; DELETE UNWANTED INDEX 298 Q 299 ; 300 MKLASD(OUTBUF,INARY) ; CREATE A LOOKASIDE BUFFER FOR MULTILPLES 301 ; 302 N ZI,ZN,ZA,ZLINE,ZLINE2,CUR,CUR2 303 F ZI=1:1:LCNT-1 D ; PROCESS THE ENTIRE ARRAY 304 . S ZLINE=@IZXML@(ZI) 305 . I ZI<LCNT S ZLINE2=@IZXML@(ZI+1) 306 . I ZLINE?.E1"</"1.E D ; NEXT LINE CONTAINS END OF A SECTION 307 . . S CUR=$$XNAME(ZLINE) ; EXTRACT THE NAME 308 . . I (ZLINE2?.E1"<"1.E)&(ZLINE'["?>") D ; BEGINNING OF A SECTION 309 . . . S CUR2=$$XNAME(ZLINE2) ; EXTRACT THE NAME 310 . . . I CUR=CUR2 D ; IF THIS IS A MULTIPLE 311 . . . . S OUTBUF(CUR,ZI+1)="" 312 ;ZWR OUTBUF 313 S ZI="" 314 F S ZI=$O(OUTBUF(ZI)) Q:ZI="" D ; FOR EACH KIND OF MULTIPLE 315 . S ZN=$O(OUTBUF(ZI,"")) ; LINE NUMBER OF SECOND MULTIPLE 316 . F S ZN=$O(@IZXML@(ZN),-1) Q:ZN="" I $E($P(@IZXML@(ZN),"<"_ZI,2),1,1)=">" Q ; 317 . S OUTBUF(ZI,ZN)="" 318 S ZA=1,ZI="",ZN="" 319 F S ZI=$O(OUTBUF(ZI)) Q:ZI="" D ; ADDING THE COUNT FOR THE MULIPLES [x] 320 . S ZN="",ZA=1 321 . F S ZN=$O(OUTBUF(ZI,ZN)) Q:ZN="" D ; 322 . . S OUTBUF(ZI,ZN)="["_ZA_"]" 323 . . S ZA=ZA+1 324 Q 325 ; 326 CLEAN(STR,TR) ; extrinsic function; returns string 327 ;; Removes all non printable characters from a string. 328 ;; STR by Value 329 ;; TR IS OPTIONAL TO IMPROVE PERFORMANCE 330 N TR,I 331 I '$D(TR) D ; 332 . F I=0:1:31 S TR=$G(TR)_$C(I) 333 . S TR=TR_$C(127) 334 QUIT $TR(STR,TR) 335 ; 336 QUERY(IARY,XPATH,OARY) ; RETURNS THE XML ARRAY MATCHING THE XPATH EXPRESSION 337 ; XPATH IS OF THE FORM "//FIRST/SECOND/THIRD" 338 ; IARY AND OARY ARE PASSED BY NAME 339 I '$D(@IARY@("INDEXED")) D ; INDEX IS NOT PRESENT IN IARY 340 . D INDEX(IARY) ; GENERATE AN INDEX FOR THE XML 341 N FIRST,LAST ; FIRST AND LAST LINES OF ARRAY TO RETURN 342 N TMP,I,J,QXPATH 343 S FIRST=1 344 I '$D(@IARY@(0)) D ; LINE COUNT NOT IN ZERO NODE 345 . S @IARY@(0)=$O(@IARY@("//"),-1) ; THIS SHOULD USUALLY WORK 346 S LAST=@IARY@(0) ; FIRST AND LAST DEFAULT TO ROOT 347 I XPATH'="//" D ; NOT A ROOT QUERY 348 . S TMP=@IARY@(XPATH) ; LOOK UP LINE VALUES 349 . S FIRST=$P(TMP,"^",1) 350 . S LAST=$P(TMP,"^",2) 351 K @OARY 352 S @OARY@(0)=+LAST-FIRST+1 353 S J=1 354 FOR I=FIRST:1:LAST D 355 . S @OARY@(J)=@IARY@(I) ; COPY THE LINE TO OARY 356 . S J=J+1 357 ; ZWR OARY 358 Q 359 ; 360 XF(IDX,XPATH) ; EXTRINSIC TO RETURN THE STARTING LINE FROM AN XPATH 361 ; INDEX WITH TWO PIECES START^FINISH 362 ; IDX IS PASSED BY NAME 363 Q $P(@IDX@(XPATH),"^",1) 364 ; 365 XL(IDX,XPATH) ; EXTRINSIC TO RETURN THE LAST LINE FROM AN XPATH 366 ; INDEX WITH TWO PIECES START^FINISH 367 ; IDX IS PASSED BY NAME 368 Q $P(@IDX@(XPATH),"^",2) 369 ; 370 START(ISTR) ; EXTRINSIC TO RETURN THE STARTING LINE FROM AN INDEX 371 ; TYPE STRING WITH THREE PIECES ARRAY;START;FINISH 372 ; COMPANION TO FINISH ; IDX IS PASSED BY NAME 373 Q $P(ISTR,";",2) 374 ; 375 FINISH(ISTR) ; EXTRINSIC TO RETURN THE LAST LINE FROM AN INDEX 376 ; TYPE STRING WITH THREE PIECES ARRAY;START;FINISH 377 Q $P(ISTR,";",3) 378 ; 379 ARRAY(ISTR) ; EXTRINSIC TO RETURN THE ARRAY REFERENCE FROM AN INDEX 380 ; TYPE STRING WITH THREE PIECES ARRAY;START;FINISH 381 Q $P(ISTR,";",1) 382 ; 383 BUILD(BLIST,BDEST) ; A COPY MACHINE THAT TAKE INSTRUCTIONS IN ARRAY BLIST 384 ; WHICH HAVE ARRAY;START;FINISH AND COPIES THEM TO DEST 385 ; DEST IS CLEARED TO START 386 ; USES PUSH TO DO THE COPY 387 N I 388 K @BDEST 389 F I=1:1:@BLIST@(0) D ; FOR EACH INSTRUCTION IN BLIST 390 . N J,ATMP 391 . S ATMP=$$ARRAY(@BLIST@(I)) 392 . I $G(DEBUG) W "ATMP=",ATMP,! 393 . I $G(DEBUG) W @BLIST@(I),! 394 . F J=$$START(@BLIST@(I)):1:$$FINISH(@BLIST@(I)) D ; 395 . . ; FOR EACH LINE IN THIS INSTR 396 . . I $G(DEBUG) W "BDEST= ",BDEST,! 397 . . I $G(DEBUG) W "ATMP= ",@ATMP@(J),! 398 . . D PUSH(BDEST,@ATMP@(J)) 399 Q 400 ; 401 QUEUE(BLST,ARRAY,FIRST,LAST) ; ADD AN ENTRY TO A BLIST 402 ; 403 I $G(DEBUG) W "QUEUEING ",BLST,! 404 D PUSH(BLST,ARRAY_";"_FIRST_";"_LAST) 405 Q 406 ; 407 CP(CPSRC,CPDEST) ; COPIES CPSRC TO CPDEST BOTH PASSED BY NAME 408 ; KILLS CPDEST FIRST 409 N CPINSTR 410 I $G(DEBUG) W "MADE IT TO COPY",CPSRC,CPDEST,! 411 I @CPSRC@(0)<1 D ; BAD LENGTH 412 . W "ERROR IN COPY BAD SOURCE LENGTH: ",CPSRC,! 413 . Q 414 ; I '$D(@CPDEST@(0)) S @CPDEST@(0)=0 ; IF THE DEST IS EMPTY, INIT 415 D QUEUE("CPINSTR",CPSRC,1,@CPSRC@(0)) ; BLIST FOR ENTIRE ARRAY 416 D BUILD("CPINSTR",CPDEST) 417 Q 418 ; 419 QOPEN(QOBLIST,QOXML,QOXPATH) ; ADD ALL BUT THE LAST LINE OF QOXML TO QOBLIST 420 ; WARNING NEED TO DO QCLOSE FOR SAME XML BEFORE CALLING BUILD 421 ; QOXPATH IS OPTIONAL - WILL OPEN INSIDE THE XPATH POINT 422 ; USED TO INSERT CHILDREN NODES 423 I @QOXML@(0)<1 D ; MALFORMED XML 424 . W "MALFORMED XML PASSED TO QOPEN: ",QOXML,! 425 . Q 426 I $G(DEBUG) W "DOING QOPEN",! 427 N S1,E1,QOT,QOTMP 428 S S1=1 ; OPEN FROM THE BEGINNING OF THE XML 429 I $D(QOXPATH) D ; XPATH PROVIDED 430 . D QUERY(QOXML,QOXPATH,"QOT") ; INSURE INDEX 431 . S E1=$P(@QOXML@(QOXPATH),"^",2)-1 432 I '$D(QOXPATH) D ; NO XPATH PROVIDED, OPEN AT ROOT 433 . S E1=@QOXML@(0)-1 434 D QUEUE(QOBLIST,QOXML,S1,E1) 435 ; S QOTMP=QOXML_"^"_S1_"^"_E1 436 ; D PUSH(QOBLIST,QOTMP) 437 Q 438 ; 439 QCLOSE(QCBLIST,QCXML,QCXPATH) ; CLOSE XML AFTER A QOPEN 440 ; ADDS THE LIST LINE OF QCXML TO QCBLIST 441 ; USED TO FINISH INSERTING CHILDERN NODES 442 ; QCXPATH IS OPTIONAL - IF PROVIDED, WILL CLOSE UNTIL THE END 443 ; IF QOPEN WAS CALLED WITH XPATH, QCLOSE SHOULD BE TOO 444 I @QCXML@(0)<1 D ; MALFORMED XML 445 . W "MALFORMED XML PASSED TO QCLOSE: ",QCXML,! 446 I $G(DEBUG) W "GOING TO CLOSE",! 447 N S1,E1,QCT,QCTMP 448 S E1=@QCXML@(0) ; CLOSE UNTIL THE END OF THE XML 449 I $D(QCXPATH) D ; XPATH PROVIDED 450 . D QUERY(QCXML,QCXPATH,"QCT") ; INSURE INDEX 451 . S S1=$P(@QCXML@(QCXPATH),"^",2) ; REMAINING XML 452 I '$D(QCXPATH) D ; NO XPATH PROVIDED, CLOSE AT ROOT 453 . S S1=@QCXML@(0) 454 D QUEUE(QCBLIST,QCXML,S1,E1) 455 ; D PUSH(QCBLIST,QCXML_";"_S1_";"_E1) 456 Q 457 ; 458 INSERT(INSXML,INSNEW,INSXPATH) ; INSERT INSNEW INTO INSXML AT THE 459 ; INSXPATH XPATH POINT INSXPATH IS OPTIONAL - IF IT IS 460 ; OMITTED, INSERTION WILL BE AT THE ROOT 461 ; NOTE INSERT IS NON DESTRUCTIVE AND WILL ADD THE NEW 462 ; XML AT THE END OF THE XPATH POINT 463 ; INSXML AND INSNEW ARE PASSED BY NAME INSXPATH IS A VALUE 464 N INSBLD,INSTMP 465 I $G(DEBUG) W "DOING INSERT ",INSXML,INSNEW,INSXPATH,! 466 I $G(DEBUG) F G1=1:1:@INSXML@(0) W @INSXML@(G1),! 467 I '$D(@INSXML@(1)) D ; INSERT INTO AN EMPTY ARRAY 468 . D CP^C0CXPATH(INSNEW,INSXML) ; JUST COPY INTO THE OUTPUT 469 I $D(@INSXML@(1)) D ; IF ORIGINAL ARRAY IS NOT EMPTY 470 . I '$D(@INSXML@(0)) S @INSXML@(0)=$O(@INSXML@(""),-1) ;SET LENGTH 471 . I $D(INSXPATH) D ; XPATH PROVIDED 472 . . D QOPEN("INSBLD",INSXML,INSXPATH) ; COPY THE BEFORE 473 . . I $G(DEBUG) D PARY^C0CXPATH("INSBLD") 474 . I '$D(INSXPATH) D ; NO XPATH PROVIDED, OPEN AT ROOT 475 . . D QOPEN("INSBLD",INSXML,"//") ; OPEN WITH ROOT XPATH 476 . I '$D(@INSNEW@(0)) S @INSNEW@(0)=$O(@INSNEW@(""),-1) ;SIZE OF XML 477 . D QUEUE("INSBLD",INSNEW,1,@INSNEW@(0)) ; COPY IN NEW XML 478 . I $D(INSXPATH) D ; XPATH PROVIDED 479 . . D QCLOSE("INSBLD",INSXML,INSXPATH) ; CLOSE WITH XPATH 480 . I '$D(INSXPATH) D ; NO XPATH PROVIDED, CLOSE AT ROOT 481 . . D QCLOSE("INSBLD",INSXML,"//") ; CLOSE WITH ROOT XPATH 482 . D BUILD("INSBLD","INSTMP") ; PUT RESULTS IN INDEST 483 . D CP^C0CXPATH("INSTMP",INSXML) ; COPY BUFFER TO SOURCE 484 Q 485 ; 486 INSINNER(INNXML,INNNEW,INNXPATH) ; INSERT THE INNER XML OF INNNEW 487 ; INTO INNXML AT THE INNXPATH XPATH POINT 488 ; 489 N INNBLD,UXPATH 490 N INNTBUF 491 S INNTBUF=$NA(^TMP($J,"INNTBUF")) 492 I '$D(INNXPATH) D ; XPATH NOT PASSED 493 . S UXPATH="//" ; USE ROOT XPATH 494 I $D(INNXPATH) S UXPATH=INNXPATH ; USE THE XPATH THAT'S PASSED 495 I '$D(@INNXML@(0)) D ; INNXML IS EMPTY 496 . D QUEUE^C0CXPATH("INNBLD",INNNEW,2,@INNNEW@(0)-1) ; JUST INNER 497 . D BUILD("INNBLD",INNXML) 498 I @INNXML@(0)>0 D ; NOT EMPTY 499 . D QOPEN("INNBLD",INNXML,UXPATH) ; 500 . D QUEUE("INNBLD",INNNEW,2,@INNNEW@(0)-1) ; JUST INNER XML 501 . D QCLOSE("INNBLD",INNXML,UXPATH) 502 . D BUILD("INNBLD",INNTBUF) ; BUILD TO BUFFER 503 . D CP(INNTBUF,INNXML) ; COPY BUFFER TO DEST 504 Q 505 ; 506 INSB4(XDEST,XNEW) ; INSERT XNEW AT THE BEGINNING OF XDEST 507 ; BUT XDEST AN XNEW ARE PASSED BY NAME 508 N XBLD,XTMP 509 D QUEUE("XBLD",XDEST,1,1) ; NEED TO PRESERVE SECTION ROOT 510 D QUEUE("XBLD",XNEW,1,@XNEW@(0)) ; ALL OF NEW XML FIRST 511 D QUEUE("XBLD",XDEST,2,@XDEST@(0)) ; FOLLOWED BY THE REST OF SECTION 512 D BUILD("XBLD","XTMP") ; BUILD THE RESULT 513 D CP("XTMP",XDEST) ; COPY TO THE DESTINATION 514 I $G(DEBUG) D PARY("XDEST") 515 Q 516 ; 517 REPLACE(REXML,RENEW,REXPATH) ; REPLACE THE XML AT THE XPATH POINT 518 ; WITH RENEW - NOTE THIS WILL DELETE WHAT WAS THERE BEFORE 519 ; REXML AND RENEW ARE PASSED BY NAME XPATH IS A VALUE 520 ; THE DELETED XML IS PUT IN ^TMP($J,"REPLACE_OLD") 521 N REBLD,XFIRST,XLAST,OLD,XNODE,RETMP 522 S OLD=$NA(^TMP($J,"REPLACE_OLD")) 523 D QUERY(REXML,REXPATH,OLD) ; CREATE INDEX, TEST XPATH, MAKE OLD 524 S XNODE=@REXML@(REXPATH) ; PULL OUT FIRST AND LAST LINE PTRS 525 S XFIRST=$P(XNODE,"^",1) 526 S XLAST=$P(XNODE,"^",2) 527 I RENEW="" D ; WE ARE DELETING A SECTION, MUST SAVE THE TAG 528 . D QUEUE("REBLD",REXML,1,XFIRST) ; THE BEFORE 529 . D QUEUE("REBLD",REXML,XLAST,@REXML@(0)) ; THE REST 530 I RENEW'="" D ; NEW XML IS NOT NULL 531 . D QUEUE("REBLD",REXML,1,XFIRST-1) ; THE BEFORE 532 . D QUEUE("REBLD",RENEW,1,@RENEW@(0)) ; THE NEW 533 . D QUEUE("REBLD",REXML,XLAST+1,@REXML@(0)) ; THE REST 534 I $G(DEBUG) W "REPLACE PREBUILD",! 535 I $G(DEBUG) D PARY("REBLD") 536 D BUILD("REBLD","RTMP") 537 K @REXML ; KILL WHAT WAS THERE 538 D CP("RTMP",REXML) ; COPY IN THE RESULT 539 Q 540 ; 541 DELETE(REXML,REXPATH) ; DELETE THE XML AT THE XPATH POINT 542 ; REXML IS PASSED BY NAME XPATH IS A VALUE 543 N REBLD,XFIRST,XLAST,OLD,XNODE,RETMP 544 S OLD=$NA(^TMP($J,"REPLACE_OLD")) 545 D QUERY(REXML,REXPATH,OLD) ; CREATE INDEX, TEST XPATH, MAKE OLD 546 S XNODE=@REXML@(REXPATH) ; PULL OUT FIRST AND LAST LINE PTRS 547 S XFIRST=$P(XNODE,"^",1) 548 S XLAST=$P(XNODE,"^",2) 549 D QUEUE("REBLD",REXML,1,XFIRST-1) ; THE BEFORE 550 D QUEUE("REBLD",REXML,XLAST+1,@REXML@(0)) ; THE REST 551 I $G(DEBUG) D PARY("REBLD") 552 D BUILD("REBLD","RTMP") 553 K @REXML ; KILL WHAT WAS THERE 554 D CP("RTMP",REXML) ; COPY IN THE RESULT 555 Q 556 ; 557 MISSING(IXML,OARY) ; SEARTH THROUGH INXLM AND PUT ANY @@X@@ VARS IN OARY 558 ; W "Reporting on the missing",! 559 ; W OARY 560 I '$D(@IXML@(0)) W "MALFORMED XML PASSED TO MISSING",! Q 561 N I 562 S @OARY@(0)=0 ; INITIALIZED MISSING COUNT 563 F I=1:1:@IXML@(0) D ; LOOP THROUGH WHOLE ARRAY 564 . I @IXML@(I)?.E1"@@".E D ; MISSING VARIABLE HERE 565 . . D PUSH^C0CXPATH(OARY,$P(@IXML@(I),"@@",2)) ; ADD TO OUTARY 566 . . Q 567 Q 568 ; 569 MAP(IXML,INARY,OXML) ; SUBSTITUTE MULTIPLE @@X@@ VARS WITH VALUES IN INARY 570 ; AND PUT THE RESULTS IN OXML 571 N XCNT 572 I '$D(DEBUG) S DEBUG=0 573 I '$D(IXML) W "MALFORMED XML PASSED TO MAP",! Q 574 I '$D(@IXML@(0)) D ; INITIALIZE COUNT 575 . S XCNT=$O(@IXML@(""),-1) 576 E S XCNT=@IXML@(0) ;COUNT 577 I $O(@INARY@(""))="" W "EMPTY ARRAY PASSED TO MAP",! Q 578 N I,J,TNAM,TVAL,TSTR 579 S @OXML@(0)=XCNT ; TOTAL LINES IN OUTPUT 580 F I=1:1:XCNT D ; LOOP THROUGH WHOLE ARRAY 581 . S @OXML@(I)=@IXML@(I) ; COPY THE LINE TO OUTPUT 582 . I @OXML@(I)?.E1"@@".E D ; IS THERE A VARIABLE HERE? 583 . . S TSTR=$P(@IXML@(I),"@@",1) ; INIT TO PART BEFORE VARS 584 . . F J=2:2:10 D Q:$P(@IXML@(I),"@@",J+2)="" ; QUIT IF NO MORE VARS 585 . . . I DEBUG W "IN MAPPING LOOP: ",TSTR,! 586 . . . S TNAM=$P(@OXML@(I),"@@",J) ; EXTRACT THE VARIABLE NAME 587 . . . S TVAL="@@"_$P(@IXML@(I),"@@",J)_"@@" ; DEFAULT UNCHANGED 588 . . . I $D(@INARY@(TNAM)) D ; IS THE VARIABLE IN THE MAP? 589 . . . . I '$D(@INARY@(TNAM,"F")) D ; NOT A SPECIAL FIELD 590 . . . . . S TVAL=@INARY@(TNAM) ; PULL OUT MAPPED VALUE 591 . . . . E D DOFLD ; PROCESS A FIELD 592 . . . S TVAL=$$SYMENC^MXMLUTL(TVAL) ;MAKE SURE THE VALUE IS XML SAFE 593 . . . S TSTR=TSTR_TVAL_$P(@IXML@(I),"@@",J+1) ; ADD VAR AND PART AFTER 594 . . S @OXML@(I)=TSTR ; COPY LINE WITH MAPPED VALUES 595 . . I DEBUG W TSTR 596 I DEBUG W "MAPPED",! 597 Q 598 ; 599 DOFLD ; PROCESS A FILEMAN FIELD REFERENCED BY A VARIABLE 600 ; 601 Q 602 ; 603 TRIM(THEXML) ; TAKES OUT ALL NULL ELEMENTS 604 ; THEXML IS PASSED BY NAME 605 N I,J,TMPXML,DEL,FOUND,INTXT 606 S FOUND=0 607 S INTXT=0 608 I $G(DEBUG) W "DELETING EMPTY ELEMENTS",! 609 F I=1:1:(@THEXML@(0)-1) D ; LOOP THROUGH ENTIRE ARRAY 610 . S J=@THEXML@(I) 611 . I J["<text>" D 612 . . S INTXT=1 ; IN HTML SECTION, DON'T TRIM 613 . . I $G(DEBUG) W "IN HTML SECTION",! 614 . N JM,JP,JPX ; JMINUS AND JPLUS 615 . S JM=@THEXML@(I-1) ; LINE BEFORE 616 . I JM["</text>" S INTXT=0 ; LEFT HTML SECTION,START TRIM 617 . S JP=@THEXML@(I+1) ; LINE AFTER 618 . I INTXT=0 D ; IF NOT IN AN HTML SECTION 619 . . S JPX=$TR(JP,"/","") ; REMOVE THE SLASH 620 . . I J=JPX D ; AN EMPTY ELEMENT ON TWO LINES 621 . . . I $G(DEBUG) W I,J,JP,! 622 . . . S FOUND=1 ; FOUND SOMETHING TO BE DELETED 623 . . . S DEL(I)="" ; SET LINE TO DELETE 624 . . . S DEL(I+1)="" ; SET NEXT LINE TO DELETE 625 . . I J["><" D ; AN EMPTY ELEMENT ON ONE LINE 626 . . . I $G(DEBUG) W I,J,! 627 . . . S FOUND=1 ; FOUND SOMETHING TO BE DELETED 628 . . . S DEL(I)="" ; SET THE EMPTY LINE UP TO BE DELETED 629 . . . I JM=JPX D ; 630 . . . . I $G(DEBUG) W I,JM_J_JPX,! 631 . . . . S DEL(I-1)="" 632 . . . . S DEL(I+1)="" ; SET THE SURROUNDING LINES FOR DEL 633 ; . I J'["><" D PUSH("TMPXML",J) 634 I FOUND D ; NEED TO DELETE THINGS 635 . F I=1:1:@THEXML@(0) D ; COPY ARRAY LEAVING OUT DELELTED LINES 636 . . I '$D(DEL(I)) D ; IF THE LINE IS NOT DELETED 637 . . . D PUSH("TMPXML",@THEXML@(I)) ; COPY TO TMPXML ARRAY 638 . D CP("TMPXML",THEXML) ; REPLACE THE XML WITH THE COPY 639 Q FOUND 640 ; 641 UNMARK(XSEC) ; REMOVE MARKUP FROM FIRST AND LAST LINE OF XML 642 ; XSEC IS A SECTION PASSED BY NAME 643 N XBLD,XTMP 644 D QUEUE("XBLD",XSEC,2,@XSEC@(0)-1) ; BUILD LIST FOR INNER XML 645 D BUILD("XBLD","XTMP") ; BUILD THE RESULT 646 D CP("XTMP",XSEC) ; REPLACE PASSED XML 647 Q 648 ; 649 PARY(GLO,ZN) ;PRINT AN ARRAY 650 ; IF ZN=-1 NO LINE NUMBERS 651 N I 652 F I=1:1:@GLO@(0) D ; 653 . I $G(ZN)=-1 W @GLO@(I),! 654 . E W I_" "_@GLO@(I),! 655 Q 656 ; 657 H2ARY(IARYRTN,IHASH,IPRE) ; CONVERT IHASH TO RETURN ARRAY 658 ; IPRE IS OPTIONAL PREFIX FOR THE ELEMENTS. USED FOR MUPTIPLES 1^"VAR"^VALUE 659 I '$D(IPRE) S IPRE="" 660 N H2I S H2I="" 661 ; W $O(@IHASH@(H2I)),! 662 F S H2I=$O(@IHASH@(H2I)) Q:H2I="" D ; FOR EACH ELEMENT OF THE HASH 663 . I $QS(H2I,$QL(H2I))="M" D Q ; SPECIAL CASE FOR MULTIPLES 664 . . ;W H2I_"^"_@IHASH@(H2I),! 665 . . N IH,IHI 666 . . S IH=$NA(@IHASH@(H2I)) ; 667 . . S IH2A=$O(@IH@("")) ; SKIP OVER MULTIPLE DISCRIPTOR 668 . . S IH2=$NA(@IH@(IH2A)) ; PAST THE "M","DIRETIONS" FOR EXAMPLE 669 . . S IHI="" ; INDEX INTO "M" MULTIPLES 670 . . F S IHI=$O(@IH2@(IHI)) Q:IHI="" D ; FOR EACH SUB-MULTIPLE 671 . . . ; W @IH@(IHI) 672 . . . S IH3=$NA(@IH2@(IHI)) 673 . . . ; W "HEY",IH3,! 674 . . . D H2ARY(.IARYRTN,IH3,IPRE_";"_IHI) ; RECURSIVE CALL - INDENTED ELEMENTS 675 . . ; W IH,! 676 . . ; W "C0CZZ",! 677 . . ; W $NA(@IHASH@(H2I)),! 678 . . Q ; 679 . D PUSH(IARYRTN,IPRE_"^"_H2I_"^"_@IHASH@(H2I)) 680 . ; W @IARYRTN@(0),! 681 Q 682 ; 683 XVARS(XVRTN,XVIXML) ; RETURNS AN ARRAY XVRTN OF ALL UNIQUE VARIABLES 684 ; DEFINED IN INPUT XML XVIXML BY @@VAR@@ 685 ; XVRTN AND XVIXML ARE PASSED BY NAME 686 ; 687 N XVI,XVTMP,XVT 688 F XVI=1:1:@XVIXML@(0) D ; FOR ALL LINES OF THE XML 689 . S XVT=@XVIXML@(XVI) 690 . I XVT["@@" S XVTMP($P(XVT,"@@",2))=XVI 691 D H2ARY(XVRTN,"XVTMP") 692 Q 693 ; 694 DXVARS(DXIN) ;DISPLAY ALL VARIABLES IN A TEMPLATE 695 ; IF PARAMETERS ARE NULL, DEFAULTS TO CCR TEMPLATE 696 ; 697 N DXUSE,DTMP ; DXUSE IS NAME OF VARIABLE, DTMP IS VARIABLE IF NOT SUPPLIED 698 I DXIN="CCR" D ; NEED TO GO GET CCR TEMPLATE 699 . D LOAD^C0CCCR0("DTMP") ; LOAD CCR TEMPLATE INTO DXTMP 700 . S DXUSE="DTMP" ; DXUSE IS NAME 701 E I DXIN="CCD" D ; NEED TO GO GET CCD TEMPLATE 702 . D LOAD^C0CCCD1("DTMP") ; LOAD CCR TEMPLATE INTO DXTMP 703 . S DXUSE="DTMP" ; DXUSE IS NAME 704 E S DXUSE=DXIN ; IF PASSED THE TEMPLATE TO USE 705 N DVARS ; PUT VARIABLE NAME RESULTS IN ARRAY HERE 706 D XVARS("DVARS",DXUSE) ; PULL OUT VARS 707 D PARY^C0CXPATH("DVARS") ;AND DISPLAY THEM 708 Q 709 ; 710 TEST ; Run all the test cases 711 D TESTALL^C0CUNIT("C0CXPAT0") 712 Q 713 ; 714 ZTEST(WHICH) ; RUN ONE SET OF TESTS 715 N ZTMP 716 S DEBUG=1 717 D ZLOAD^C0CUNIT("ZTMP","C0CXPAT0") 718 D ZTEST^C0CUNIT(.ZTMP,WHICH) 719 Q 720 ; 721 TLIST ; LIST THE TESTS 722 N ZTMP 723 D ZLOAD^C0CUNIT("ZTMP","C0CXPAT0") 724 D TLIST^C0CUNIT(.ZTMP) 725 Q 726 ;
Note:
See TracChangeset
for help on using the changeset viewer.
