���� JFIF  XX �� �� �     $.' ",#(7),01444'9=82<.342  2!!22222222222222222222222222222222222222222222222222�� ��" �� 4     ��   �� �,�PG"Z_�4�˷����kjز�Z�,F+��_z�,�© �����zh6�٨�ic�fu��� #ډb���_�N� ?� �wQ���5-�~�I���8��� �TK<5o�Iv-� ����k�_U_����� ~b�M��d��� �Ӝ�U�Hh��?]��E�w��Q���k�{��_}qFW7HTՑ��Y��F� ?_�'ϔ��_�Ջt� �=||I �� 6�έ"�����D���/[�k�9�� �Y�8 ds|\���Ҿp6�Ҵ���]��.����6� z<�v��@]�i% �� $j��~ �g��J>��no����pM[me�i$[�� �� s�o�ᘨ�˸ nɜG-�ĨU�ycP� 3.DB�li�;� �hj���x 7Z^�N�h��� ���N3u{�:j �x�힞��#M &��jL P@ _���� P�� &��o8 ������9 �����@Sz 6�t7#O�ߋ � s}Yf�T� ��lmr����Z)'N��k�۞p ����w\�T ȯ?�8` �O��i{wﭹW�[�r�� ��Q4F�׊�� �3m&L�=��h3� ���z~��#� \�l :�F,j@�� ʱ�wQT����8�"kJO��� 6�֚l���� }��� R�>ډK���]��y����&����p�}b�� ;N�1�m�r$� |��7�>e�@ B�TM*-i H��g�D�)� E�m�|�ؘbҗ�a ��Ҿ���� t4��� o���G��*oCN�rP���Q��@z,|?W[0 �����:�n,j WiE��W� �$~/�hp\��?��{(�0���+�Y8rΟ�+����>S-S�� ��VN;� }�s?.����� w �9��˟<���Mq4�Wv' ��{)0�1mB ��V����W[� ����8�/<� �%���wT^�5���b��)iM� p g�N�&ݝ� �VO~� q���u���9� ����!��J27��� �$ O-���! �: �%H��� ـ ����y�ΠM=t{!S�� oK8������ t<����è :a�� ����[���� �ա�H���~��w��Qz`�p o�^ �� ��Q��n�  �,uu�C� $ ^���,� �����8�#��:�6��e�|~� ��!�3� 3.�\0�� q��o�4`.|� ����y�Q�`~;�d�ׯ,��O�Zw�������`73�v�܋�< ���Ȏ�� ـ4k��5�K�a�u�=9Yd��$>x�A�&�� j0� ���vF��� Y� |�y��� ~�6�@c��1vOp �Ig�� ��4��l�OD� ��L����� R���c���j�_�uX 6��3?nk��Wy�f;^*B� ��@ �~a�`��Eu������ +� �� 6�L��.ü>��}y���}_�O�6�͐�:�Yr G�X��kG�� ���l^w�� �~㒶sy� �Iu�!� W ��X��N�7BV��O��!X�2����wvG�R�f�T#�����t�/?���%8�^�W�aT ��G�cL�M���I��(J����1~�8�?aT ���]����AS�E��(��*E}� 2�� #I/�׍qz��^t�̔��� b�Yz4x ���t�){ OH� �+(E��A&�N�������XT��o��"�XC�� '���)}�J�z�p� ��~5�}�^����+�6����w��c��Q�| Lp�d�H��}�(�.|����k��c4^� "�����Z?ȕ ��a< �L�!0 39C� �Eu� C�F�Ew�ç ;�n?�*o���B�8�bʝ���'#Rqf�� �M}7����]��� �s2tcS{�\icTx;�\��7K���P ���ʇ Z O-��~�� c>"��?�� �����P ��E��O�8��@�8��G��Q�g�a�Վ���󁶠 �䧘��_%#r�>� 1�z�a�� eb��qcP ѵ��n���#L��� =��׀t� L�7�` ��V��� A{�C:�g���e@ �w1 Xp 3�c3�ġ���� p��M"'-�@n4���fG� �B3�DJ�8[Jo�ߐ���gK)ƛ��$���� � ��8�3�����+���� �����6�ʻ���� ���S�kI�*KZlT _`�� �?��K� ���QK�d ����B`�s}�>���` ��*�>��,*@J�d�oF*� ���弝��O}�k��s��]��y�ߘ ��c1G�V���<=�7��7����6 �q�PT��tXԀ�!9*4�4Tހ 3XΛex�46�� �Y��D ����� �BdemDa����\�_l,� �G�/���֌7���Y�](�xTt^%�GE�����4�}bT ���ڹ�����; Y)���B�Q��u��>J/J � ⮶.�XԄ��j�ݳ� +E��d ��r�5�_D �1 �� o�� �B�x�΢�#� ��<��W�����8���R6�@ g�M�.��� dr�D��>(otU��@ x=��~v���2� ӣ�d�oBd ��3�eO�6�㣷�� ���ݜ 6��6Y��Qz`�� S��{���\P �~z m5{J/L��1������<�e�ͅPu� b�]�ϔ ���'�� ����f�b� Zpw��c`"��i���BD@:)ִ�:�]��h v�E� w���T�l ��P� ��"Ju�}��وV J��G6��. J/�Qgl߭�e�����@�z�Zev2u� )]կ��� ��7x�� �s�M�-<ɯ�c��r� v�����@��$�ޮ}lk���a�� �'����>x��O\�Z Fu>��� ��ck#��&:��`�$ �ai�>2Δ����l���oF[h� �lE�ܺ�Π k:)���` �� $[6�����9�����kOw�\|��� 8}������ބ:��񶐕� �I�A1/� =�2[�,�!��.}gN#�u����b ��� ~� �݊��}34q��� �d�E��L c��$ ��"�[q�U�硬g^��%B � z���r�p J�ru%v\h 1Y�ne` ǥ:g�� �pQM~�^� Xi� ��`S�:V2 9.�P���V� ?B�k�� AEvw%�_�9C�Q����wKekP ؠ�\� ;Io d�{ ߞo�c1eP��� �\� `����E=���@K<�Y�� �eڼ�J ���w����{av�F�'�M�@ /J��+9p ���|]���� �Iw &` ��8���& M�hg ��[�{ ��Xj�� %��Ӓ� $��(��� �ʹN��� <>�I���RY� ��K2�NPlL�ɀ )��&e� ���B+ь����( � �JTx ���_?EZ� }@ 6�U���뙢ط�z��dWI� n` D����噥�[��uV��"�G& Ú����2 g�}&m� �?ċ �"����Om#� ������� � ��{� ON��"S�X ��Ne��ysQ���@ Fn��Vg��� dX�~nj� ]J�<�K]: ��FW�� b�������62 �=��5f����JKw� �bf�X� 55��~J �%^� ���:�-�QIE��P��v�nZum� z � ~ə ���� ���ة����;�f��\v��� g�8�1��f2 4;�V���ǔ�)��� �9���1\�� c��v�/'Ƞ�w����� ��$�4�R-��t�� �� e�6�/�ġ �̕Ecy�J���u�B���<�W�ַ~�w[B1L۲�-JS΂�{���΃���� ��A��20�c# �� @    0!1@AP"#2Q`$3V�%45a6�FRUq���   � ���^7ׅ,$n� ������+��F�`��2X'��0vM��p�L=������ 5��8������u�p~���.�`r�����\��� O��,ư�0oS ��_�M�����l���4�kv\JSd���x���SW�<��Ae�IX����������$I���w�:S���y���›R��9�Q[���,�5�;�@]�%���u�@ *ro�lbI �� ��+���%m:�͇ZV�����u�̉����θau<�fc�.����{�4Ա� �Q����*�Sm��8\ujqs]{kN���)qO�y�_*dJ�b�7���yQqI&9�ԌK!�M}�R�;�� ����S�T���1���i[U�ɵz�]��U)V�S6���3$K{� ߊ<�(� E]Զ[ǼENg�����'�\?#)Dkf��J���o��v���'�%ƞ�&K�u� !��b�35LX�Ϸ��63$K�a�;�9>,R��W��3�3� d�JeTYE.Mϧ��-�o�j3+y��y^�c�������VO�9NV\nd�1 ��!͕_)a�v;����թ�M�lWR1��)El��P;��yوÏ�u 3�k�5Pr6<�⒲l�!˞*��u־�n�!�l:����UNW ��%��Chx8vL'��X�@��*��)���̮��ˍ��� � ��D-M�+J�U�kvK����+�x8��cY������?�Ԡ��~3mo��|�u@[XeY�C�\Kp�x8�oC�C�&����N�~3-H���� ��MX�s�u<`���~"WL��$8ξ��3���a�)|:@�m�\���^�`�@ҷ)�5p+��6���p�%i)P M���ngc�����#0Aruz���RL+xSS?���ʮ}()#�t��mˇ!��0}}y����<�e� �-ή�Ԩ��X������ MF���ԙ~l L.3���}�V뽺�v��� ��멬��Nl�)�2����^�Iq��a��M��qG��T�����c3#������3U�Ǎ���}��לS�|qa��ڃ�+���-��2�f����/��bz��ڐ�� �ݼ[2�ç����k�X�2�* �Z�d���J�G����M*9W���s{��w���T��x��y,�in�O�v��]���n����P�$� JB@=4�OTI�n��e�22a\����q�d���%�$��(���:���: /*�K[PR�fr\nڙdN���F�n�$�4� [�� U�zƶ����� �mʋ���,�ao�u 3�z� �x��Kn����\[��VFmbE;�_U��&V�Gg�]L�۪&#n%�$ɯ� dG���D�TI=�%+AB�Ru#��b4�1�»x�cs�YzڙJG��f��Il� �d�eF'T� iA��T���uC�$����Y��H?����[!G`}���ͪ� �纤Hv\������j�Ex�K���!���OiƸ�Yj�+u-<���'q����uN�*�r\��+�]���<�wOZ.fp�ێ��,-*)V?j-kÊ#�`�r��dV����(�ݽBk�����G�ƛk�QmUڗe��Z���f}|����8�8��a���i��3'J�����~G_�^���d�8w������ R�`(�~�.��u���l�s+g�bv���W���lGc}��u���afE~1�Ue������Z�0�8�=e�� f@/�jqEKQQ�J� �oN��J���W5~M>$6�Lt�;$ʳ{���^��6�{����v6���ķܰg�V�cnn �~z�x�«�,2�u�?cE+Ș�H؎�%�Za�)���X>uW�Tz�Nyo����s���FQƤ��$��*�&�LLXL)�1�" L��eO��ɟ�9=���:t��Z���c��Ž���Y?�ӭV�wv�~,Y��r�ۗ�|�y��GaF�����C�����.�+� ���v1���fήJ�����]�S��T��B��n5sW}y�$��~z�'�c ��8 ��� ,! �p��VN�S��N�N�q��y8z˱�A��4��*��'������2n<�s���^ǧ˭P�Jޮɏ�U�G�L�J�*#��<�V��t7�8����TĜ>��i}K%,���)[��z�21z ?�N�i�n1?T�I�R#��m-�����������������1����lA�`��fT5+��ܐ�c�q՝��ʐ��,���3�f2U�եmab��#ŠdQ�y>\��)�SLY����w#��.���ʑ�f��� ,"+�w�~�N�'�c�O�3F�������N<���)j��&��,-� �љ���֊�_�zS���TǦ����w�>��?�������n��U仆�V���e�����0���$�C�d���rP �m�׈e�Xm�Vu� �L��.�bֹ��� �[Դaզ���*��\y�8�Է:�Ez\�0�Kq�C b��̘��cө���Q��=0Y��s�N��S.��� 3.���O�o:���#���v7�[#߫ ��5�܎�L���Er4���9n��COWlG�^��0k�%<���ZB���aB_���������'=��{i�v�l�$�uC���mƎҝ{�c㱼�y]���W�i ��ߧc��m�H� m�"�"�����;Y�ߝ�Z�Ǔ�����:S#��|}�y�,/k�Ld� TA�(�AI$+I3��;Y*���Z��}|��ӧO��d�v��..#:n��f>�>���ȶI�TX��� 8��y����"d�R�|�)0���=���n4��6ⲑ�+��r<�O�܂~zh�z����7ܓ�HH�Ga롏���nCo�>������a ���~]���R���̲c?�6(�q�;5%� |�uj�~z8R =X��I�V=�|{v�Gj\gc��q����z�؋%M�ߍ����1y��#��@f^���^�>N��� ��#x#۹��6�Y~�?�dfPO��{��P�4��V��u1E1J �*|���%�� �JN��`eWu�zk M6���q t[�� ��g�G���v��WIG��u_ft����5�j�"�Y�:T��ɐ���*�;� e5���4����q$C��2d�}���� _S�L#m�Yp��O�.�C�;��c����Hi#֩%+) �Ӎ��ƲV���SYź��g |���tj��3�8���r|���V��1#;.SQ�A[���S������#���`n�+���$��$ I �P\[�@�s��(�ED�z���P��])8�G#��0B��[ى��X�II�q<��9�~[Z멜�Z�⊔IWU&A>�P~�#��dp<�?����7���c��'~���5 ��+$���lx@�M�dm��n<=e�dyX��?{�|Aef ,|n3�<~z�ƃ�uۧ�����P��Y,�ӥQ�*g�#먙R�\���;T��i,��[9Qi歉����c>]9�� ��"�c��P�� �Md?٥��If�ت�u��k��/����F��9�c*9��Ǎ:�ØF���z�n*�@|I�ށ9����N3{'��[�'ͬ�Ҳ4��#}��!�V� Fu��,�,mTIk���v C�7v���B�6k�T9��1�*l� '~��ƞF��lU��'�M ����][ΩũJ_�{�i�I�n��$�� �L�� j��O�dx�����kza۪��#�E��Cl����x˘�o�����V���ɞ�ljr��)�/,�߬h�L��#��^��L�ф�,íMƁe�̩�NB�L�����iL����q�}��(��q��6IçJ$�W�E$��:������=#����(�K�B����zђ <��K(�N�۫K�w��^O{!����) �H���>x�������lx�?>Պ�+�>�W���,Ly!_�D���Ō�l���Q�!�[ �S����J��1��Ɛ�Y}��b,+�Lo�x�ɓ)����=�y�oh�@�꥟/��I��ѭ=��P�y9��� �ۍYӘ�e+�p�Jnϱ?V\SO%�(�t� ���=?MR�[Ș�����d�/ ��n�l��B�7j� ��!�;ӥ�/�[-���A�>� dN�sLj ��,ɪv��=1c�.SQ�O3�U���ƀ�ܽ�E����������̻��9G�ϷD�7(�}��Ävӌ\� y�_0[w ���<΍>����a_��[0+�L��F.�޺��f�>oN�T����q;���y\��bՃ��y�jH�<|q-eɏ�_?_9+P���Hp$�����[ux�K w�Mw��N�ی'$Y2�=��q���KB��P��~�� ����Yul:�[<����F1�2�O���5=d����]Y�sw:���Ϯ���E��j,_Q��X��z`H1,#II ��d�wr��P˂@�ZJV����y$�\y�{}��^~���[:N����ߌ�U�������O��d�����ؾe��${p>G��3c���Ė�lʌ�� ת��[��`ϱ�-W����dg�I��ig2��� ��}s ��ؤ(%#sS@���~���3�X�nRG�~\jc3�v��ӍL��M[JB�T��s3}��j�Nʖ��W����;7� �ç?=X�F=-�=����q�ߚ���#���='�c��7���ڑW�I(O+=:uxq�������������e2�zi+�kuG�R��������0�&e�n���iT^J����~\jy���p'dtG��s����O��3����9* �b#Ɋ�� p������[Bws�T�>d4�ۧs���nv�n���U���_�~,�v����ƜJ1��s�� �QIz�� )�(lv8M���U=�;����56��G���s#�K���MP�=��LvyGd��}�VwWBF�'�à �?MH�U�g2�� ����!�p�7Q��j��ڴ����=��j�u��� Jn�A s���uM������e��Ɔ�Ҕ�!) '��8Ϣ�ٔ� �ޝ(��Vp���צ֖d=�IC�J�Ǡ{q������kԭ�߸���i��@K����u�|�p=..�*+����x�����z[Aqġ#s2a�Ɗ���RR�)*HRsi�~�a &f��M��P����-K�L@��Z��Xy�'x�{}��Zm+���:�)�) IJ�-i�u���� ���ܒH��'� L(7�y�GӜq���� j��� 6ߌg1�g�o���,kر���tY�?W,���p���e���f�OQS��!K�۟cҒA�|ս�j�>��=⬒��˧L[�� �߿2JaB~R��u�:��Q�] �0H~���]�7��Ƽ�I���( }��cq '�ήET���q�?f�ab���ӥvr� �)o��-Q��_'����ᴎo��K������;��V���o��%���~OK ����*��b�f:���-ťIR��`B�5!RB@���ï�� �u �̯e\�_U�_������� g�ES��3������� QT��a�� ��x����U<~�c?�*�#]�MW,[8O�a�x��]�1bC|踤�P��lw5V%�)�{t�<��d��5���0i�XSU��m:��Z�┵�i�"��1�^B�-��P�hJ��&)O��*�D��c�W��vM��)����}���P��ܗ-q����\mmζZ-l@�}��a��E�6��F�@��&Sg@���ݚ�M����� ȹ 4����#p�\H����dYDo�H���"��\��..R�B�H�z_�/5˘����6��KhJR��P�mƶi�m���3� ,#c�co��q�a)*P t����R�m�k�7x�D�E�\Y�閣_X�<���~�)���c[[�BP����6�Yq���S��0����%_����;��Àv�~�| VS؇ ��'O0��F0��\���U�-�d@�����7�SJ*z��3n��y��P����O��������� m�~�P�3|Y��ʉr#�C�<�G~�.,! ���bqx���h~0=��!ǫ�jy����l� O,�[B��~��|9��ٱ����Xly�#�i�B��g%�S��������tˋ���e���ې��\[d�t)��.+u�|1 ������#�~Oj����hS�%��i.�~X���I�H�m��0n���c�1uE�q��cF�RF�o���7� �O�ꮧ� ���ۛ{��ʛi5�rw?׌#Qn�TW��~?y$��m\�\o����%W� ?=>S�N@�� �Ʈ���R����N�)�r"C�:��:����� �����#��qb��Y�. �6[��2K����2u�Ǧ�HYR��Q�MV��� �G�$��Q+.>�����nNH��q�^��� ����q��mM��V��D�+�-�#*�U�̒ ���p욳��u:�������IB���m� ��PV@O���r[b= �� ��1U�E��_Nm�yKbN�O���U�}�the�`�|6֮P>�\2�P�V���I�D�i�P�O;�9�r�mAHG�W�S]��J*�_�G��+kP�2����Ka�Z���H�'K�x�W�MZ%�O�YD�Rc+o��?�q��Ghm��d�S�oh�\�D�|:W������UA�Qc yT�q� �����~^�H��/��#p�CZ���T�I�1�ӏT����4��"�ČZ�����}��`w�#�*,ʹ�� ��0�i��課�Om�*�da��^gJ݅{���l�e9uF#T�ֲ��̲�ٞC"�q���ߍ ոޑ�o#�XZTp����@ o�8��(jd��xw�]�,f���`~� |,s��^����f�1���t��|��m�򸄭/ctr��5s��7�9Q�4�H1꠲BB@ l9@���C�����+�wp�xu�£Yc�9��?`@#�o�mH�s2��)�=��2�.�l����jg�9$�Y�S�%*L������R�Y������7Z���,*=�䷘$�������arm�o�ϰ���UW.|�r�uf����IGw�t����Zwo��~5 ��YյhO+=8fF�)�W�7�L9lM�̘·Y���֘YLf�큹�pRF���99.A �"wz��=E\Z���'a� 2��Ǚ�#;�'}�G���*��l��^"q��+2FQ� hj��kŦ��${���ޮ-�T�٭cf�|�3#~�RJ����t��$b�(R��(����r���dx� >U b�&9,>���%E\� Ά�e�$��'�q't��*�א���ެ�b��-|d���SB�O�O��$�R+�H�)�܎�K��1m`;�J�2�Y~9��O�g8=vqD`K[�F)k�[���1m޼c��n���]s�k�z$@��)!I �x՝"v��9=�ZA=`Ɠi �:�E��)` 7��vI��}d�YI�_ �o�:ob���o ���3Q��&D&�2=�� �Ά��;>�h����y.*ⅥS������Ӭ�+q&����j|UƧ��� �}���J0��WW< ۋS�)jQR�j���Ư��rN)�Gű�4Ѷ(�S)Ǣ�8��i��W52���No˓� ۍ%�5brOn�L�;�n��\G����=�^U�dI���8$�&���h��'���+�(������cȁ߫k�l��S^���cƗjԌE�ꭔ��gF���Ȓ��@���}O���*;e�v�WV���YJ\�]X'5��ղ�k�F��b 6R�o՜m��i N�i���� >J����?��lPm�U��}>_Z&�KK��q�r��I�D�Չ~�q�3fL�:S�e>���E���-G���{L�6p�e,8��������QI��h��a�Xa��U�A'���ʂ���s�+טIjP�-��y�8ۈZ?J$��W�P� ��R�s�]��|�l(�ԓ��sƊi��o(��S0 ��Y� 8�T97.�����WiL��c�~�dxc�E|�2!�X�K�Ƙਫ਼�$((�6�~|d9u+�qd�^3�89��Y�6L�.I�����?���iI�q���9�)O/뚅����O���X��X�V��ZF[�یgQ�L��K1���RҖr@v�#��X�l��F���Нy�S�8�7�kF!A��sM���^rkp�jP�DyS$N���q�� nxҍ!U�f�!eh�i�2�m ���`�Y�I�9r�6� �TF���C}/�y�^���Η���5d�'��9A-��J��>{�_l+�`��A���[�'��յ�ϛ#w:݅�%��X�}�&�PSt�Q�"�-��\縵�/����$Ɨh�Xb�*�y��BS����;W�ջ_mc�����vt?2}1�;qS�d�d~u:2k5�2�R�~�z+|HE!)�Ǟl��7`��0�<�,�2*���Hl-��x�^����'_TV�gZA�'j� ^�2Ϊ��N7t�����?w�� �x1��f��Iz�C-Ȗ��K�^q�;���-W�DvT�7��8�Z�������� hK�(P:��Q- �8�n�Z���܃e貾�<�1�YT<�,�����"�6{ / �?�͟��|1�:�#g��W�>$����d��J��d�B�� =��jf[��%rE^��il:��B���x���Sּ�1հ��,�=��*�7 fcG��#q� �eh?��2�7�����,�!7x��6�n�LC�4x��},Geǝ�tC.��vS �F�43��zz\��;QYC,6����~;RYS/6���|2���5���v��T��i����������mlv��������&� �nRh^ejR�LG�f���? �ۉҬܦƩ��|��Ȱ����>3����!v��i�ʯ�>�v��オ�X3e���_1z�Kȗ\<������!�8���V��]��?b�k41�Re��T�q��mz��TiOʦ�Z��Xq���L������q"+���2ۨ��8}�&N7XU7Ap�d�X��~�׿��&4e�o�F��� �H�� ��O���č�c�� 懴�6���͉��+)��v;j��ݷ�� �UV�� i��� j���Y9GdÒJ1��詞�����V?h��l�� ��l�cGs�ځ�������y�Ac���� �\V3�? �� ܙg�>qH�S,�E�W�[�㺨�uch�⍸�O�}���a��>�q�6�n6� ���N6�q�� ���� N    ! 1AQaq�0@����"2BRb�#Pr���3C`��Scst���$4D���%Td��  ? � ��N����a��3��m���C���w��������xA�m�q�m��� m������$����4n淿t'��C"w��zU=D�\R+w�p+Y�T�&�պ@��ƃ��3ޯ?�Aﶂ��aŘ���@-�����Q�=���9D��ռ�ѻ@��M�V��P��܅�G5�f�Y<�u=,EC)�<�Fy'�"�&�չ�X~f��l�KԆV��?�� �W�N����=(� �;���{�r����ٌ�Y���h{�١������jW����P���Tc�����X�K�r��}���w�R��%��?���E��m�� �Y�q|����\lEE4� ��r���}�lsI�Y������f�$�=�d�yO����p�����yBj8jU�o�/�S��?�U��*������ˍ�0����� �u�q�m [�?f����a�� )Q�>����6#������� ?����0UQ����,IX���(6ڵ[�DI�MNލ�c&���υ�j\��X�R|,4��� j������T�hA�e��^���d���b<����n�� �즇�=!���3�^�`j�h�ȓr��jẕ�c�,ٞX����-����a�ﶔ���#�$��]w�O��Ӫ�1y%��L�Y<�wg#�ǝ�̗`�x�xa�t�w��»1���o7o5��>�m뭛C���Uƃߜ}�C���y1Xνm�F8�jI���]����H���ۺиE@I�i;r�8ӭ���� V�F�Շ| ��&?�3|x�B�MuS�Ge�=Ӕ�#BE5G�� ���Y!z��_e��q�р/W>|-�Ci߇�t�1ޯќd�R3�u��g�=0 5��[?�#͏��q�cf���H��{ ?u�=?�?ǯ���}Z��z���hmΔ�BFTW�����<�q� (v� ��!��z���iW]*�J�V�z��gX֧A�q�&��/w���u�gYӘa���; �i=����g:��?2�dž6�ى�k�4�>�Pxs����}������G�9� �3 ���)gG�R<>r h�$��'nc�h�P��Bj��J�ҧH� -��N1���N��?��~��}-q!=��_2hc�M��l�vY%UE�@|�v����M2�.Y[|y�"Eï��K�ZF,�ɯ?,q�?v�M 80jx�"�;�9vk�����+ ֧�� �ȺU��?�%�vcV��mA�6��Qg^M��� �A}�3�nl� QRN�l8�kkn�'�����(��M�7m9و�q���%ޟ���*h$Zk"��$�9��: �?U8�Sl��,,|ɒ��xH(ѷ����Gn�/Q�4�P��G�%��Ա8�N��!� �&�7�;���eKM7�4��9R/%����l�c>�x;������>��C�:�����t��h?aKX�bhe�ᜋ^�$�Iհ �hr7%F$�E��Fd���t��5���+�(M6�t����Ü�UU|zW�=a�Ts�Tg������dqP�Q����b'�m���1{|Y����X�N��b �P~��F^F:����k6�"�j!�� �I�r�`��1&�-$�Bevk:y���#y w��I0��x��=D�4��tU���P�ZH��ڠ底taP��6����b>�xa� ���Q�#� WeF��ŮNj�p�J* mQ�N��� �*I�-*�ȩ�F�g�3 �5��V�ʊ�ɮ�a��5F���O@{���NX��?����H�]3��1�Ri_u��������ѕ�� ����0��� F��~��:60�p�͈�S��qX#a�5>���`�o&+�<2�D����: �������ڝ�$�nP���*)�N�|y�Ej�F�5ټ�e���ihy�Z �>���k�bH�a�v��h�-#���!�Po=@k̆IEN��@��}Ll?j�O������߭�ʞ���Q|A07x���wt!xf���I2?Z��<ץ�T���cU�j��]�� 陎Ltl �}5�ϓ��$�,��O�mˊ�;�@O��jE��j(�ا,��LX���LO���Ц�90�O �.����a��nA���7������j4 ��W��_ٓ���zW�jcB������y՗+EM�)d���N�g6�y1_x��p�$Lv :��9�"z��p���ʙ$��^��JԼ*�ϭ����o���=x�Lj�6�J��u82�A�H�3$�ٕ@�=Vv�]�'�qEz�;I˼��)��=��ɯ���x �/�W(V���p�����$ �m�������u�����񶤑Oqˎ�T����r��㠚x�sr�GC��byp�G��1ߠ�w e�8�$⿄����/�M{*}��W�]˷.�CK\�ުx���/$�WP w���r� |i���&�}�{�X� �>��$-��l���?-z���g����lΆ���(F���h�vS*���b���߲ڡn,|)mrH[���a�3�ר�[1��3o_�U�3�TC�$��(�=�)0�kgP���� ��u�^=��4 �WYCҸ:��vQ�ר�X�à��tk�m,�t*��^�,�}D*� �"(�I��9R����>`�`��[~Q]�#af��i6l��8���6�:,s�s�N6�j"�A4���IuQ��6E,�GnH��zS�HO�uk�5$�I�4��ؤ�Q9�@��C����wp �BGv[]�u�Ov��� 0I4���\��y�����Q�Ѹ��~>Z��8�T��a��q�ޣ;z��a���/��S��I:�ܫ_�|������>=Z����8:�S��U�I�J��"IY���8%b8���H��:�QO�6�;7�I�S��J��ҌAά3��>c���E+&jf$eC+�z�;��V����� �r���ʺ������my�e���aQ�f&��6�ND ��.:��NT�vm�<- u���ǝ\MvZY�N�NT��-A�>jr!S��n�O 1�3�Ns�%�3D@���`������ܟ 1�^c<���� �a�ɽ�̲�Xë#�w�|y�cW�=�9I*H8�p�^(4���՗�k��arOcW�tO�\�ƍR��8����'�K���I�Q�����?5�>[�}��yU�ײ -h��=��% q�ThG�2�)���"ו3]�!kB��*p�FDl�A���,�eEi�H�f�Ps�����5�H:�Փ~�H�0Dت�D�I����h�F3�������c��2���E��9�H��5�zԑ�ʚ�i�X�=:m�xg�hd(�v����׊�9iS��O��d@0ڽ���:�p�5�h-��t�&���X�q�ӕ,��ie�|���7A�2���O%P��E��htj��Y1��w�Ѓ!����  ���� ࢽ��My�7�\�a�@�ţ�J �4�Ȼ�F�@o�̒?4�wx��)��]�P��~�����u�����5�����7X ��9��^ܩ�U;Iꭆ 5 �������eK2�7(�{|��Y׎ �V��\"���Z�1� Z�����}��(�Ǝ"�1S���_�vE30>���p;� ΝD��%x�W�?W?v����o�^V�i�d��r[��/&>�~`�9Wh��y�;���R�� � ;;ɮT��?����r$�g1�K����A��C��c��K��l:�'��3 c�ﳯ*"t8�~l��)���m��+U,z��`( �>yJ�?����h>��]��v��ЍG*�{`��;y]��I�T� ;c��NU�fo¾h���/$���|NS���1�S�"�H��V���T���4��uhǜ�]�v;���5�͠x��'C\�SBpl���h}�N����� A�Bx���%��ޭ�l��/����T��w�ʽ]D�=����K���ž�r㻠l4�S�O?=�k �M:� ��c�C�a�#ha���)�ѐxc�s���gP�iG�� {+���x���Q���I= �� z��ԫ+ �8"�k�ñ�j=|����c ��y��CF��/ ��*9ж�h{ �?4�o� ��k�m�Q�N�x��;�Y��4膚�a�w?�6�> e]�����Q�r�:����g�,i"�����ԩA� *M�<�G��b�if��l^M��5� �Ҩ�{����6J��ZJ�����P�*�����Y���ݛu�_4�9�I8�7���������,^ToR���m4�H��?�N�S�ѕw��/S��甍�@�9H�S�T��t�ƻ���ʒU��*{Xs�@����f��� ��֒Li�K{H�w^���������Ϥm�tq���s� ���ք��f:��o~s��g�r��ט� �S�ѱC�e]�x���a��) ���(b-$(�j>�7q�B?ӕ�F��hV25r[7 Y� }L�R��}����*sg+��x�r�2�U=�*'WS��ZDW]�WǞ�<��叓���{�$�9Ou4��y�90-�1�'*D`�c�^o?(�9��u���ݐ��'PI&� f�Jݮ�������:wS����jfP1F:X �H�9dԯ�� �˝[�_54 �}*;@�ܨ�� ð�yn�T���?�ןd�#���4rG�ͨ��H�1�|-#���Mr�S3��G�3�����)�.᧏3v�z֑��r����$G"�`j �1t��x0<Ɔ�Wh6�y�6��,œ�Ga��gA����y��b��)� �h�D��ß�_�m��ü �gG;��e�v��ݝ�nQ� ��C����-�*��o���y�a��M��I�>�<���]obD��"�:���G�A��-\%LT�8���c�)��+y76���o�Q�#*{�(F�⽕�y����=���rW�\p���۩�c���A���^e6��K������ʐ�cVf5$�'->���ՉN"���F�"�UQ@�f��Gb~��#�&�M=��8�ט�JNu9��D��[̤�s�o�~��� ��� G��9T�tW^g5y$b��Y'��س�Ǵ�=��U-2 #�MC�t(�i� �lj�@Q 5�̣i�*�O����s�x�K�f��}\��M{E�V�{�υ��Ƈ�����);�H����I��fe�Lȣr�2��>��W� I�Ȃ6������i��k�� �5�YOxȺ����>��Y�f5'��|��H+��98pj�n�.O�y�������jY��~��i�w'������l�;�s�2��Y��:'lg�ꥴ)o#'Sa�a�K��Z� �m��}�`169�n���"���x��I ��*+� }F<��cГ���F�P�������ֹ*�PqX�x۩��,� ��N�� �4<-����%����:��7����W���u�`����� $�?�I��&����o��o��`v�>��P��"��l���4��5'�Z�gE���8���?��[�X�7(��.Q�-��*���ތL@̲����v��.5���[��=�t\+�CNܛ��,g�SQnH����}*F�G16���&:�t��4ُ"A��̣��$�b �|����#rs��a�����T�� ]�<�j��B S�('$�ɻ� �wP;�/�n��?�ݜ��x�F��yUn�~mL*-�������Xf�wd^�a�}��f�,=t�׵i�.2/wpN�Ep8�OР���•��R�FJ� 55TZ��T �ɭ�<��]��/�0�r�@�f��V��V����Nz�G��^���7hZi����k��3�,kN�e|�vg�1{9]_i��X5y7� 8e]�U����'�-2,���e"����]ot�I��Y_��n�(JҼ��1�O ]bXc���Nu�No��pS���Q_���_�?i�~�x h5d'�(qw52] ��'ޤ�q��o1�R!���`ywy�A4u���h<קy���\[~�4�\ X�Wt/� 6�����n�F�a8��f���z �3$�t(���q��q�x��^�XWeN'p<-v�!�{�(>ӽDP7��ո0�y)�e$ٕv�Ih'Q�EA�m*�H��RI��=:��� ���4牢) �%_iN�ݧ�l]� �Nt���G��H�L��� ɱ�g<���1V�,�J~�ٹ�"K��Q�� 9�HS�9�?@��k����r�;we݁�]I�!{ �@�G�[�"��`���J:�n]�{�cA�E����V��ʆ���#��U9�6����j�#Y�m\��q�e4h�B�7��C�������d<�?J����1g:ٳ���=Y���D�p�ц� ׈ǔ��1�]26؜oS�'��9�V�FVu�P�h�9�xc�oq�X��p�o�5��Ա5$�9W�V(�[Ak�aY錎qf;�'�[�|���b�6�Ck��)��#a#a˙��8���=äh�4��2��C��4tm^ �n'c� ��]GQ$[Wҿ��i���vN�{Fu ��1�gx��1┷���N�m��{j-,��x�� Ūm�ЧS�[�s���Gna���䑴�� x�p 8<������97�Q���ϴ�v�aϚG��Rt�Һ׈�f^\r��WH�JU�7Z���y)�vg=����n��4�_)y��D'y�6�]�c�5̪ �\� �PF�k����&�c;��cq�$~T�7j ���nç]�<�g ":�to�t}�159�<�/�8������m�b�K#g'I'.W����� 6��I/��>v��\�MN��g���m�A�yQL�4u�Lj�j9��#44�t��l^�}L����n��R��!��t��±]��r��h6ٍ>�yҏ�N��fU�� ���� Fm@�8}�/u��jb9������he:A�y�ծw��GpΧh�5����l}�3p468��)U��d��c����;Us/�֔�YX�1�O2��uq�s��`hwg�r~�{ R��mhN��؎*q 42�*th��>�#���E����#��Hv�O����q�}����� 6�e��\�,Wk�#���X��b>��p}�դ��3���T5��†��6��[��@ �P�y*n��|'f�֧>�lư΂�̺����SU�'*�q�p�_S�����M�� '��c�6��� ��m�� ySʨ;M��r���Ƌ�m�Kxo,���Gm�P��A�G�:��i��w�9�}M(�^�V��$ǒ�ѽ�9���|���� �a����J�SQ�a���r�B;����}���ٻ֢�2�%U���c�#�g���N�a�ݕ�'�v�[�OY'��3L�3�;,p�]@�S��{ls��X�'���c�jw� k'a�.��}�}&�� �dP�*�bK=ɍ!����;3n�gΊU�ߴmt�'*{,=SzfD� A��ko~�G�aoq�_mi}#�m�������P�Xhύ��� �mxǍ�΂���巿zf��Q���c���|kc�����?���W��Y�$���_Lv����l߶��c���`?����l�j�ݲˏ!V��6����U�Ђ(A���4y)H���p�Z_�x��>���e�� R��$�/�`^'3qˏ�-&Q�=?��CFVR �D�fV�9��{�8g�������n�h�(P"��6�[�D���< E�����~0<@�`�G�6����Hг�cc�� �c�K.5��D��d�B���`?�XQ��2��ٿyqo&+�1^� DW�0�ꊩ���G�#��Q�nL3��c���������/��x ��1�1 [y�x�პCW��C�c�UĨ80�m�e�4.{�m��u���I=��f�����0QRls9���f���������9���~f�����Ǩ��a�"@�8���ȁ�Q����#c�ic������G��$���G���r/$W�(��W���V�"��m�7�[m�A�m����bo��D� j����۳� l���^�k�h׽����� ��#� iXn�v��eT�k�a�^Y�4�BN�� ĕ�� 0    !01@Q"2AaPq3BR������ ? � ��@4�Q�����T3,���㺠�W�[=JK�Ϟ���2�r^7��vc�:�9 �E�ߴ�w�S#d���Ix��u��:��Hp��9E!�� V 2;73|F��9Y���*ʬ�F��D����u&���y؟��^EA��A��(ɩ���^��GV:ݜDy�`��Jr29ܾ�㝉��[���E;Fzx��YG��U�e�Y�C���� ����v-tx����I�sם�Ę�q��Eb�+P\ :>�i�C'�;�����k|z�رn�y]�#ǿb��Q��������w�����(�r|ӹs��[�D��2v-%��@;�8<a���[\o[ϧw��I!��*0�krs)�[�J9^��ʜ��p1)� "��/_>��o��<1����A�E�y^�C��`�x1'ܣn�p��s`l���fQ��):�l����b>�Me�jH^?�kl3(�z:���1ŠK&?Q�~�{�ٺ�h�y���/�[��V�|6��}�KbX����mn[-��7�5q�94�������dm���c^���h� X��5��<�eޘ>G���-�}�دB�ޟ� ��|�rt�M��V+�]�c?�-#ڛ��^ǂ}���Lkr���O��u�>�-D�ry� D?:ޞ�U��ǜ�7�V��?瓮�"�#���r��չģVR;�n���/_� ؉v�ݶe5d�b9��/O��009�G���5n�W����JpA�*�r9�>�1��.[t���s�F���nQ� V 77R�]�ɫ8����_0<՜�IF�u(v��4��F�k�3��E)��N:��yڮe��P�`�1}�$WS��J�SQ�N�j �ٺ��޵�#l���ј(�5=��5�lǏmoW�v-�1����v,W�mn��߀$x�<����v�j(����c]��@#��1������Ǔ���o'��u+����;G�#�޸��v-lη��/(`i⣍Pm^� ��ԯ̾9Z��F��������n��1��� ��]�[��)�'������ :�֪�W��FC����� �B9،!?���]��V��A�Վ�M��b�w��G F>_DȬ0¤�#�QR�[V��kz���m�w�"��9ZG�7'[��=�Q����j8R?�zf�\a�=��O�U����*oB�A�|G���2�54 �p��.w7� �� ��&������ξxGHp� B%��$g�����t�Џ򤵍z���HN�u�Я�-�'4��0�� ;_�� 3     !01"@AQa2Pq#3BR������ ? � �ʩca��en��^��8���<�u#��m*08r��y�N"�<�Ѳ0��@\�p��� �����Kv�D��J8�Fҽ� �f�Y��-m�ybX�NP����}�!*8t(�OqѢ��Q�wW�K��ZD��Δ^e��!� ��B�K��p~�����e*l}z#9ң�k���q#�Ft�o��S�R����-�w�!�S���Ӥß|M�l޶V��!eˈ�8Y���c�ЮM2��tk���� ������J�fS����Ö*i/2�����n]�k�\���|4yX�8��U�P.���Ы[���l��@"�t�<������5�lF���vU�����W��W��;�b�cД^6[#7@vU�xgZv��F�6��Q,K�v��� �+Ъ��n��Ǣ��Ft���8��0��c�@�!�Zq s�v�t�;#](B��-�nῃ~���3g������5�J�%���O������n�kB�ĺ�.r��+���#�N$?�q�/�s�6��p��a����a��J/��M�8��6�ܰ"�*������ɗud"\w���aT(����[��F��U՛����RT�b���n�*��6���O��SJ�.�ij<�v�MT��R\c��5l�sZB>F��<7�;EA��{��E���Ö��1U/�#��d1�a�n.1ě����0�ʾR�h��|�R��Ao�3�m3 ��%�� ���28Q� ��y��φ���H�To�7�lW>����#i`�q���c����a��� �m,B�-j����݋�'mR1Ήt�>��V��p���s�0IbI�C.���1R�ea�����]H�6�������� ��4B>��o��](��$B���m�����a�!=� �?�B� K�Ǿ+�Ծ"�n���K��*��+��[T#�{ E�J�S����Q�����s�5�:�U�\wĐ�f�3����܆&�)��� �I���Ԇw��E T�lrTf6Q|R�h:��[K�� �z��c֧�G�C��%\��_�a �84��HcO�bi��ؖV��7H �)*ģK~Xhչ0��4?�0��� �E<���}3���#���u�?�� ��|g�S�6ꊤ�|�I#Hڛ� �ա��w�X��9��7���Ŀ%�SL��y6č��|�F�a 8���b� �$�sק�h���b9RAu7�˨p�Č�_\*w��묦��F ����4D~�f����|(�"m���NK��i�S�>�$d7SlA��/�²����SL��|6N�}���S�˯���g��]6��; �#�.��<���q'Q�1|KQ$�����񛩶"�$r�b:���N8�w@��8$�� �AjfG|~�9F ���Y��ʺ��Bwؒ������M:I岎�G��`s�YV5����6��A �b:�W���G�q%l�����F��H���7�������Fsv7� �k�� 403WebShell
403Webshell
Server IP : 127.0.0.1  /  Your IP : 10.100.1.254
Web Server : Apache/2.4.58 (Win64) OpenSSL/3.1.3 PHP/8.0.30
System : Windows NT WIZC-EXTRANET 10.0 build 19045 (Windows 10) AMD64
User : SYSTEM ( 0)
PHP Version : 8.0.30
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : OFF  |  Perl : OFF  |  Python : OFF  |  Sudo : OFF  |  Pkexec : OFF
Directory :  C:/xampp/php_8/pear/adodb/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : C:/xampp/php_8/pear/adodb/adodb-xmlschema03.inc.php
<?php
// Copyright (c) 2004-2005 ars Cognita Inc., all rights reserved
/* ******************************************************************************
    Released under both BSD license and Lesser GPL library license. 
 	Whenever there is any discrepancy between the two licenses, 
 	the BSD license will take precedence. 
*******************************************************************************/
/**
 * xmlschema is a class that allows the user to quickly and easily
 * build a database on any ADOdb-supported platform using a simple
 * XML schema.
 *
 * Last Editor: $Author: jlim $
 * @author Richard Tango-Lowy & Dan Cech
 * @version $Revision: 1.62 $
 *
 * @package axmls
 * @tutorial getting_started.pkg
 */
 
function _file_get_contents($file) 
{
 	if (function_exists('file_get_contents')) return file_get_contents($file);
	
	$f = fopen($file,'r');
	if (!$f) return '';
	$t = '';
	
	while ($s = fread($f,100000)) $t .= $s;
	fclose($f);
	return $t;
}


/**
* Debug on or off
*/
if( !defined( 'XMLS_DEBUG' ) ) {
	define( 'XMLS_DEBUG', FALSE );
}

/**
* Default prefix key
*/
if( !defined( 'XMLS_PREFIX' ) ) {
	define( 'XMLS_PREFIX', '%%P' );
}

/**
* Maximum length allowed for object prefix
*/
if( !defined( 'XMLS_PREFIX_MAXLEN' ) ) {
	define( 'XMLS_PREFIX_MAXLEN', 10 );
}

/**
* Execute SQL inline as it is generated
*/
if( !defined( 'XMLS_EXECUTE_INLINE' ) ) {
	define( 'XMLS_EXECUTE_INLINE', FALSE );
}

/**
* Continue SQL Execution if an error occurs?
*/
if( !defined( 'XMLS_CONTINUE_ON_ERROR' ) ) {
	define( 'XMLS_CONTINUE_ON_ERROR', FALSE );
}

/**
* Current Schema Version
*/
if( !defined( 'XMLS_SCHEMA_VERSION' ) ) {
	define( 'XMLS_SCHEMA_VERSION', '0.3' );
}

/**
* Default Schema Version.  Used for Schemas without an explicit version set.
*/
if( !defined( 'XMLS_DEFAULT_SCHEMA_VERSION' ) ) {
	define( 'XMLS_DEFAULT_SCHEMA_VERSION', '0.1' );
}

/**
* How to handle data rows that already exist in a database during and upgrade.
* Options are INSERT (attempts to insert duplicate rows), UPDATE (updates existing
* rows) and IGNORE (ignores existing rows).
*/
if( !defined( 'XMLS_MODE_INSERT' ) ) {
	define( 'XMLS_MODE_INSERT', 0 );
}
if( !defined( 'XMLS_MODE_UPDATE' ) ) {
	define( 'XMLS_MODE_UPDATE', 1 );
}
if( !defined( 'XMLS_MODE_IGNORE' ) ) {
	define( 'XMLS_MODE_IGNORE', 2 );
}
if( !defined( 'XMLS_EXISTING_DATA' ) ) {
	define( 'XMLS_EXISTING_DATA', XMLS_MODE_INSERT );
}

/**
* Default Schema Version.  Used for Schemas without an explicit version set.
*/
if( !defined( 'XMLS_DEFAULT_UPGRADE_METHOD' ) ) {
	define( 'XMLS_DEFAULT_UPGRADE_METHOD', 'ALTER' );
}

/**
* Include the main ADODB library
*/
if( !defined( '_ADODB_LAYER' ) ) {
	require( 'adodb.inc.php' );
	require( 'adodb-datadict.inc.php' );
}

/**
* Abstract DB Object. This class provides basic methods for database objects, such
* as tables and indexes.
*
* @package axmls
* @access private
*/
class dbObject {
	
	/**
	* var object Parent
	*/
	var $parent;
	
	/**
	* var string current element
	*/
	var $currentElement;
	
	/**
	* NOP
	*/
	function dbObject( &$parent, $attributes = NULL ) {
		$this->parent = $parent;
	}
	
	/**
	* XML Callback to process start elements
	*
	* @access private
	*/
	function _tag_open( &$parser, $tag, $attributes ) {
		
	}
	
	/**
	* XML Callback to process CDATA elements
	*
	* @access private
	*/
	function _tag_cdata( &$parser, $cdata ) {
		
	}
	
	/**
	* XML Callback to process end elements
	*
	* @access private
	*/
	function _tag_close( &$parser, $tag ) {
		
	}
	
	function create(&$xmls) {
		return array();
	}
	
	/**
	* Destroys the object
	*/
	function destroy() {
		unset( $this );
	}
	
	/**
	* Checks whether the specified RDBMS is supported by the current
	* database object or its ranking ancestor.
	*
	* @param string $platform RDBMS platform name (from ADODB platform list).
	* @return boolean TRUE if RDBMS is supported; otherwise returns FALSE.
	*/
	function supportedPlatform( $platform = NULL ) {
		return is_object( $this->parent ) ? $this->parent->supportedPlatform( $platform ) : TRUE;
	}
	
	/**
	* Returns the prefix set by the ranking ancestor of the database object.
	*
	* @param string $name Prefix string.
	* @return string Prefix.
	*/
	function prefix( $name = '' ) {
		return is_object( $this->parent ) ? $this->parent->prefix( $name ) : $name;
	}
	
	/**
	* Extracts a field ID from the specified field.
	*
	* @param string $field Field.
	* @return string Field ID.
	*/
	function FieldID( $field ) {
		return strtoupper( preg_replace( '/^`(.+)`$/', '$1', $field ) );
	}
}

/**
* Creates a table object in ADOdb's datadict format
*
* This class stores information about a database table. As charactaristics
* of the table are loaded from the external source, methods and properties
* of this class are used to build up the table description in ADOdb's
* datadict format.
*
* @package axmls
* @access private
*/
class dbTable extends dbObject {
	
	/**
	* @var string Table name
	*/
	var $name;
	
	/**
	* @var array Field specifier: Meta-information about each field
	*/
	var $fields = array();
	
	/**
	* @var array List of table indexes.
	*/
	var $indexes = array();
	
	/**
	* @var array Table options: Table-level options
	*/
	var $opts = array();
	
	/**
	* @var string Field index: Keeps track of which field is currently being processed
	*/
	var $current_field;
	
	/**
	* @var boolean Mark table for destruction
	* @access private
	*/
	var $drop_table;
	
	/**
	* @var boolean Mark field for destruction (not yet implemented)
	* @access private
	*/
	var $drop_field = array();
	
	/**
	* @var array Platform-specific options
	* @access private
	*/
	var $currentPlatform = true;
	
	
	/**
	* Iniitializes a new table object.
	*
	* @param string $prefix DB Object prefix
	* @param array $attributes Array of table attributes.
	*/
	function dbTable( &$parent, $attributes = NULL ) {
		$this->parent = $parent;
		$this->name = $this->prefix($attributes['NAME']);
	}
	
	/**
	* XML Callback to process start elements. Elements currently 
	* processed are: INDEX, DROP, FIELD, KEY, NOTNULL, AUTOINCREMENT & DEFAULT. 
	*
	* @access private
	*/
	function _tag_open( &$parser, $tag, $attributes ) {
		$this->currentElement = strtoupper( $tag );
		
		switch( $this->currentElement ) {
			case 'INDEX':
				if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
					xml_set_object( $parser, $this->addIndex( $attributes ) );
				}
				break;
			case 'DATA':
				if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
					xml_set_object( $parser, $this->addData( $attributes ) );
				}
				break;
			case 'DROP':
				$this->drop();
				break;
			case 'FIELD':
				// Add a field
				$fieldName = $attributes['NAME'];
				$fieldType = $attributes['TYPE'];
				$fieldSize = isset( $attributes['SIZE'] ) ? $attributes['SIZE'] : NULL;
				$fieldOpts = !empty( $attributes['OPTS'] ) ? $attributes['OPTS'] : NULL;
				
				$this->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts );
				break;
			case 'KEY':
			case 'NOTNULL':
			case 'AUTOINCREMENT':
			case 'DEFDATE':
			case 'DEFTIMESTAMP':
			case 'UNSIGNED':
				// Add a field option
				$this->addFieldOpt( $this->current_field, $this->currentElement );
				break;
			case 'DEFAULT':
				// Add a field option to the table object
				
				// Work around ADOdb datadict issue that misinterprets empty strings.
				if( $attributes['VALUE'] == '' ) {
					$attributes['VALUE'] = " '' ";
				}
				
				$this->addFieldOpt( $this->current_field, $this->currentElement, $attributes['VALUE'] );
				break;
			case 'OPT':
			case 'CONSTRAINT':
				// Accept platform-specific options
				$this->currentPlatform = ( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) );
				break;
			default:
				// print_r( array( $tag, $attributes ) );
		}
	}
	
	/**
	* XML Callback to process CDATA elements
	*
	* @access private
	*/
	function _tag_cdata( &$parser, $cdata ) {
		switch( $this->currentElement ) {
			// Table/field constraint
			case 'CONSTRAINT':
				if( isset( $this->current_field ) ) {
					$this->addFieldOpt( $this->current_field, $this->currentElement, $cdata );
				} else {
					$this->addTableOpt( $cdata );
				}
				break;
			// Table/field option
			case 'OPT':
				if( isset( $this->current_field ) ) {
					$this->addFieldOpt( $this->current_field, $cdata );
				} else {
				$this->addTableOpt( $cdata );
				}
				break;
			default:
				
		}
	}
	
	/**
	* XML Callback to process end elements
	*
	* @access private
	*/
	function _tag_close( &$parser, $tag ) {
		$this->currentElement = '';
		
		switch( strtoupper( $tag ) ) {
			case 'TABLE':
				$this->parent->addSQL( $this->create( $this->parent ) );
				xml_set_object( $parser, $this->parent );
				$this->destroy();
				break;
			case 'FIELD':
				unset($this->current_field);
				break;
			case 'OPT':
			case 'CONSTRAINT':
				$this->currentPlatform = true;
				break;
			default:

		}
	}
	
	/**
	* Adds an index to a table object
	*
	* @param array $attributes Index attributes
	* @return object dbIndex object
	*/
	function addIndex( $attributes ) {
		$name = strtoupper( $attributes['NAME'] );
		$this->indexes[$name] = new dbIndex( $this, $attributes );
		return $this->indexes[$name];
	}
	
	/**
	* Adds data to a table object
	*
	* @param array $attributes Data attributes
	* @return object dbData object
	*/
	function addData( $attributes ) {
		if( !isset( $this->data ) ) {
			$this->data = new dbData( $this, $attributes );
		}
		return $this->data;
	}
	
	/**
	* Adds a field to a table object
	*
	* $name is the name of the table to which the field should be added. 
	* $type is an ADODB datadict field type. The following field types
	* are supported as of ADODB 3.40:
	* 	- C:  varchar
	*	- X:  CLOB (character large object) or largest varchar size
	*	   if CLOB is not supported
	*	- C2: Multibyte varchar
	*	- X2: Multibyte CLOB
	*	- B:  BLOB (binary large object)
	*	- D:  Date (some databases do not support this, and we return a datetime type)
	*	- T:  Datetime or Timestamp
	*	- L:  Integer field suitable for storing booleans (0 or 1)
	*	- I:  Integer (mapped to I4)
	*	- I1: 1-byte integer
	*	- I2: 2-byte integer
	*	- I4: 4-byte integer
	*	- I8: 8-byte integer
	*	- F:  Floating point number
	*	- N:  Numeric or decimal number
	*
	* @param string $name Name of the table to which the field will be added.
	* @param string $type	ADODB datadict field type.
	* @param string $size	Field size
	* @param array $opts	Field options array
	* @return array Field specifier array
	*/
	function addField( $name, $type, $size = NULL, $opts = NULL ) {
		$field_id = $this->FieldID( $name );
		
		// Set the field index so we know where we are
		$this->current_field = $field_id;
		
		// Set the field name (required)
		$this->fields[$field_id]['NAME'] = $name;
		
		// Set the field type (required)
		$this->fields[$field_id]['TYPE'] = $type;
		
		// Set the field size (optional)
		if( isset( $size ) ) {
			$this->fields[$field_id]['SIZE'] = $size;
		}
		
		// Set the field options
		if( isset( $opts ) ) {
			$this->fields[$field_id]['OPTS'] = array($opts);
		} else {
			$this->fields[$field_id]['OPTS'] = array();
		}
	}
	
	/**
	* Adds a field option to the current field specifier
	*
	* This method adds a field option allowed by the ADOdb datadict 
	* and appends it to the given field.
	*
	* @param string $field	Field name
	* @param string $opt ADOdb field option
	* @param mixed $value Field option value
	* @return array Field specifier array
	*/
	function addFieldOpt( $field, $opt, $value = NULL ) {
		if( $this->currentPlatform ) {
		if( !isset( $value ) ) {
			$this->fields[$this->FieldID( $field )]['OPTS'][] = $opt;
		// Add the option and value
		} else {
			$this->fields[$this->FieldID( $field )]['OPTS'][] = array( $opt => $value );
		}
	}
	}
	
	/**
	* Adds an option to the table
	*
	* This method takes a comma-separated list of table-level options
	* and appends them to the table object.
	*
	* @param string $opt Table option
	* @return array Options
	*/
	function addTableOpt( $opt ) {
		if(isset($this->currentPlatform)) {
			$this->opts[$this->parent->db->databaseType] = $opt;
		}
		return $this->opts;
	}

	
	/**
	* Generates the SQL that will create the table in the database
	*
	* @param object $xmls adoSchema object
	* @return array Array containing table creation SQL
	*/
	function create( &$xmls ) {
		$sql = array();
		
		// drop any existing indexes
		if( is_array( $legacy_indexes = $xmls->dict->MetaIndexes( $this->name ) ) ) {
			foreach( $legacy_indexes as $index => $index_details ) {
				$sql[] = $xmls->dict->DropIndexSQL( $index, $this->name );
			}
		}
		
		// remove fields to be dropped from table object
		foreach( $this->drop_field as $field ) {
			unset( $this->fields[$field] );
		}
		
		// if table exists
		if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) {
			// drop table
			if( $this->drop_table ) {
				$sql[] = $xmls->dict->DropTableSQL( $this->name );
				
				return $sql;
			}
			
			// drop any existing fields not in schema
			foreach( $legacy_fields as $field_id => $field ) {
				if( !isset( $this->fields[$field_id] ) ) {
					$sql[] = $xmls->dict->DropColumnSQL( $this->name, $field->name );
				}
			}
		// if table doesn't exist
		} else {
			if( $this->drop_table ) {
				return $sql;
			}
			
			$legacy_fields = array();
		}
		
		// Loop through the field specifier array, building the associative array for the field options
		$fldarray = array();
		
		foreach( $this->fields as $field_id => $finfo ) {
			// Set an empty size if it isn't supplied
			if( !isset( $finfo['SIZE'] ) ) {
				$finfo['SIZE'] = '';
			}
			
			// Initialize the field array with the type and size
			$fldarray[$field_id] = array(
				'NAME' => $finfo['NAME'],
				'TYPE' => $finfo['TYPE'],
				'SIZE' => $finfo['SIZE']
			);
			
			// Loop through the options array and add the field options. 
			if( isset( $finfo['OPTS'] ) ) {
				foreach( $finfo['OPTS'] as $opt ) {
					// Option has an argument.
					if( is_array( $opt ) ) {
						$key = key( $opt );
						$value = $opt[key( $opt )];
						@$fldarray[$field_id][$key] .= $value;
					// Option doesn't have arguments
					} else {
						$fldarray[$field_id][$opt] = $opt;
					}
				}
			}
		}
		
		if( empty( $legacy_fields ) ) {
			// Create the new table
			$sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
			logMsg( end( $sql ), 'Generated CreateTableSQL' );
		} else {
			// Upgrade an existing table
			logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" );
			switch( $xmls->upgrade ) {
				// Use ChangeTableSQL
				case 'ALTER':
					logMsg( 'Generated ChangeTableSQL (ALTERing table)' );
					$sql[] = $xmls->dict->ChangeTableSQL( $this->name, $fldarray, $this->opts );
					break;
				case 'REPLACE':
					logMsg( 'Doing upgrade REPLACE (testing)' );
					$sql[] = $xmls->dict->DropTableSQL( $this->name );
					$sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
					break;
				// ignore table
				default:
					return array();
			}
		}
		
		foreach( $this->indexes as $index ) {
			$sql[] = $index->create( $xmls );
		}
		
		if( isset( $this->data ) ) {
			$sql[] = $this->data->create( $xmls );
		}
		
		return $sql;
	}
	
	/**
	* Marks a field or table for destruction
	*/
	function drop() {
		if( isset( $this->current_field ) ) {
			// Drop the current field
			logMsg( "Dropping field '{$this->current_field}' from table '{$this->name}'" );
			// $this->drop_field[$this->current_field] = $xmls->dict->DropColumnSQL( $this->name, $this->current_field );
			$this->drop_field[$this->current_field] = $this->current_field;
		} else {
			// Drop the current table
			logMsg( "Dropping table '{$this->name}'" );
			// $this->drop_table = $xmls->dict->DropTableSQL( $this->name );
			$this->drop_table = TRUE;
		}
	}
}

/**
* Creates an index object in ADOdb's datadict format
*
* This class stores information about a database index. As charactaristics
* of the index are loaded from the external source, methods and properties
* of this class are used to build up the index description in ADOdb's
* datadict format.
*
* @package axmls
* @access private
*/
class dbIndex extends dbObject {
	
	/**
	* @var string	Index name
	*/
	var $name;
	
	/**
	* @var array	Index options: Index-level options
	*/
	var $opts = array();
	
	/**
	* @var array	Indexed fields: Table columns included in this index
	*/
	var $columns = array();
	
	/**
	* @var boolean Mark index for destruction
	* @access private
	*/
	var $drop = FALSE;
	
	/**
	* Initializes the new dbIndex object.
	*
	* @param object $parent Parent object
	* @param array $attributes Attributes
	*
	* @internal
	*/
	function dbIndex( &$parent, $attributes = NULL ) {
		$this->parent = $parent;
		
		$this->name = $this->prefix ($attributes['NAME']);
	}
	
	/**
	* XML Callback to process start elements
	*
	* Processes XML opening tags. 
	* Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH. 
	*
	* @access private
	*/
	function _tag_open( &$parser, $tag, $attributes ) {
		$this->currentElement = strtoupper( $tag );
		
		switch( $this->currentElement ) {
			case 'DROP':
				$this->drop();
				break;
			case 'CLUSTERED':
			case 'BITMAP':
			case 'UNIQUE':
			case 'FULLTEXT':
			case 'HASH':
				// Add index Option
				$this->addIndexOpt( $this->currentElement );
				break;
			default:
				// print_r( array( $tag, $attributes ) );
		}
	}
	
	/**
	* XML Callback to process CDATA elements
	*
	* Processes XML cdata.
	*
	* @access private
	*/
	function _tag_cdata( &$parser, $cdata ) {
		switch( $this->currentElement ) {
			// Index field name
			case 'COL':
				$this->addField( $cdata );
				break;
			default:
				
		}
	}
	
	/**
	* XML Callback to process end elements
	*
	* @access private
	*/
	function _tag_close( &$parser, $tag ) {
		$this->currentElement = '';
		
		switch( strtoupper( $tag ) ) {
			case 'INDEX':
				xml_set_object( $parser, $this->parent );
				break;
		}
	}
	
	/**
	* Adds a field to the index
	*
	* @param string $name Field name
	* @return string Field list
	*/
	function addField( $name ) {
		$this->columns[$this->FieldID( $name )] = $name;
		
		// Return the field list
		return $this->columns;
	}
	
	/**
	* Adds options to the index
	*
	* @param string $opt Comma-separated list of index options.
	* @return string Option list
	*/
	function addIndexOpt( $opt ) {
		$this->opts[] = $opt;
		
		// Return the options list
		return $this->opts;
	}
	
	/**
	* Generates the SQL that will create the index in the database
	*
	* @param object $xmls adoSchema object
	* @return array Array containing index creation SQL
	*/
	function create( &$xmls ) {
		if( $this->drop ) {
			return NULL;
		}
		
		// eliminate any columns that aren't in the table
		foreach( $this->columns as $id => $col ) {
			if( !isset( $this->parent->fields[$id] ) ) {
				unset( $this->columns[$id] );
			}
		}
		
		return $xmls->dict->CreateIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts );
	}
	
	/**
	* Marks an index for destruction
	*/
	function drop() {
		$this->drop = TRUE;
	}
}

/**
* Creates a data object in ADOdb's datadict format
*
* This class stores information about table data, and is called
* when we need to load field data into a table.
*
* @package axmls
* @access private
*/
class dbData extends dbObject {
	
	var $data = array();
	
	var $row;
	
	/**
	* Initializes the new dbData object.
	*
	* @param object $parent Parent object
	* @param array $attributes Attributes
	*
	* @internal
	*/
	function dbData( &$parent, $attributes = NULL ) {
		$this->parent = $parent;
	}
	
	/**
	* XML Callback to process start elements
	*
	* Processes XML opening tags. 
	* Elements currently processed are: ROW and F (field). 
	*
	* @access private
	*/
	function _tag_open( &$parser, $tag, $attributes ) {
		$this->currentElement = strtoupper( $tag );
		
		switch( $this->currentElement ) {
			case 'ROW':
				$this->row = count( $this->data );
				$this->data[$this->row] = array();
				break;
			case 'F':
				$this->addField($attributes);
			default:
				// print_r( array( $tag, $attributes ) );
		}
	}
	
	/**
	* XML Callback to process CDATA elements
	*
	* Processes XML cdata.
	*
	* @access private
	*/
	function _tag_cdata( &$parser, $cdata ) {
		switch( $this->currentElement ) {
			// Index field name
			case 'F':
				$this->addData( $cdata );
				break;
			default:
				
		}
	}
	
	/**
	* XML Callback to process end elements
	*
	* @access private
	*/
	function _tag_close( &$parser, $tag ) {
		$this->currentElement = '';
		
		switch( strtoupper( $tag ) ) {
			case 'DATA':
				xml_set_object( $parser, $this->parent );
				break;
		}
	}
	
	/**
	* Adds a field to the insert
	*
	* @param string $name Field name
	* @return string Field list
	*/
	function addField( $attributes ) {
		// check we're in a valid row
		if( !isset( $this->row ) || !isset( $this->data[$this->row] ) ) {
			return;
		}
		
		// Set the field index so we know where we are
		if( isset( $attributes['NAME'] ) ) {
			$this->current_field = $this->FieldID( $attributes['NAME'] );
		} else {
			$this->current_field = count( $this->data[$this->row] );
		}
		
		// initialise data
		if( !isset( $this->data[$this->row][$this->current_field] ) ) {
			$this->data[$this->row][$this->current_field] = '';
		}
	}
	
	/**
	* Adds options to the index
	*
	* @param string $opt Comma-separated list of index options.
	* @return string Option list
	*/
	function addData( $cdata ) {
		// check we're in a valid field
		if ( isset( $this->data[$this->row][$this->current_field] ) ) {
			// add data to field
			$this->data[$this->row][$this->current_field] .= $cdata;
		}
	}
	
	/**
	* Generates the SQL that will add/update the data in the database
	*
	* @param object $xmls adoSchema object
	* @return array Array containing index creation SQL
	*/
	function create( &$xmls ) {
		$table = $xmls->dict->TableName($this->parent->name);
		$table_field_count = count($this->parent->fields);
		$tables = $xmls->db->MetaTables(); 
		$sql = array();
		
		$ukeys = $xmls->db->MetaPrimaryKeys( $table );
		if( !empty( $this->parent->indexes ) and !empty( $ukeys ) ) {
			foreach( $this->parent->indexes as $indexObj ) {
				if( !in_array( $indexObj->name, $ukeys ) ) $ukeys[] = $indexObj->name;
			}
		}
		
		// eliminate any columns that aren't in the table
		foreach( $this->data as $row ) {
			$table_fields = $this->parent->fields;
			$fields = array();
			$rawfields = array(); // Need to keep some of the unprocessed data on hand.
			
			foreach( $row as $field_id => $field_data ) {
				if( !array_key_exists( $field_id, $table_fields ) ) {
					if( is_numeric( $field_id ) ) {
						$field_id = reset( array_keys( $table_fields ) );
					} else {
						continue;
					}
				}
				
				$name = $table_fields[$field_id]['NAME'];
				
				switch( $table_fields[$field_id]['TYPE'] ) {
					case 'I':
					case 'I1':
					case 'I2':
					case 'I4':
					case 'I8':
						$fields[$name] = intval($field_data);
						break;
					case 'C':
					case 'C2':
					case 'X':
					case 'X2':
					default:
						$fields[$name] = $xmls->db->qstr( $field_data );
						$rawfields[$name] = $field_data;
				}
				
				unset($table_fields[$field_id]);
				
			}
			
			// check that at least 1 column is specified
			if( empty( $fields ) ) {
				continue;
			}
			
			// check that no required columns are missing
			if( count( $fields ) < $table_field_count ) {
				foreach( $table_fields as $field ) {
					if( isset( $field['OPTS'] ) and ( in_array( 'NOTNULL', $field['OPTS'] ) || in_array( 'KEY', $field['OPTS'] ) ) && !in_array( 'AUTOINCREMENT', $field['OPTS'] ) ) {
							continue(2);
						}
				}
			}
			
			// The rest of this method deals with updating existing data records.
			
			if( !in_array( $table, $tables ) or ( $mode = $xmls->existingData() ) == XMLS_MODE_INSERT ) {
				// Table doesn't yet exist, so it's safe to insert.
				logMsg( "$table doesn't exist, inserting or mode is INSERT" );
			$sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')';
				continue;
		}
		
			// Prepare to test for potential violations. Get primary keys and unique indexes
			$mfields = array_merge( $fields, $rawfields );
			$keyFields = array_intersect( $ukeys, array_keys( $mfields ) );
			
			if( empty( $ukeys ) or count( $keyFields ) == 0 ) {
				// No unique keys in schema, so safe to insert
				logMsg( "Either schema or data has no unique keys, so safe to insert" );
				$sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')';
				continue;
			}
			
			// Select record containing matching unique keys.
			$where = '';
			foreach( $ukeys as $key ) {
				if( isset( $mfields[$key] ) and $mfields[$key] ) {
					if( $where ) $where .= ' AND ';
					$where .= $key . ' = ' . $xmls->db->qstr( $mfields[$key] );
				}
			}
			$records = $xmls->db->Execute( 'SELECT * FROM ' . $table . ' WHERE ' . $where );
			switch( $records->RecordCount() ) {
				case 0:
					// No matching record, so safe to insert.
					logMsg( "No matching records. Inserting new row with unique data" );
					$sql[] = $xmls->db->GetInsertSQL( $records, $mfields );
					break;
				case 1:
					// Exactly one matching record, so we can update if the mode permits.
					logMsg( "One matching record..." );
					if( $mode == XMLS_MODE_UPDATE ) {
						logMsg( "...Updating existing row from unique data" );
						$sql[] = $xmls->db->GetUpdateSQL( $records, $mfields );
					}
					break;
				default:
					// More than one matching record; the result is ambiguous, so we must ignore the row.
					logMsg( "More than one matching record. Ignoring row." );
			}
		}
		return $sql;
	}
}

/**
* Creates the SQL to execute a list of provided SQL queries
*
* @package axmls
* @access private
*/
class dbQuerySet extends dbObject {
	
	/**
	* @var array	List of SQL queries
	*/
	var $queries = array();
	
	/**
	* @var string	String used to build of a query line by line
	*/
	var $query;
	
	/**
	* @var string	Query prefix key
	*/
	var $prefixKey = '';
	
	/**
	* @var boolean	Auto prefix enable (TRUE)
	*/
	var $prefixMethod = 'AUTO';
	
	/**
	* Initializes the query set.
	*
	* @param object $parent Parent object
	* @param array $attributes Attributes
	*/
	function dbQuerySet( &$parent, $attributes = NULL ) {
		$this->parent = $parent;
			
		// Overrides the manual prefix key
		if( isset( $attributes['KEY'] ) ) {
			$this->prefixKey = $attributes['KEY'];
		}
		
		$prefixMethod = isset( $attributes['PREFIXMETHOD'] ) ? strtoupper( trim( $attributes['PREFIXMETHOD'] ) ) : '';
		
		// Enables or disables automatic prefix prepending
		switch( $prefixMethod ) {
			case 'AUTO':
				$this->prefixMethod = 'AUTO';
				break;
			case 'MANUAL':
				$this->prefixMethod = 'MANUAL';
				break;
			case 'NONE':
				$this->prefixMethod = 'NONE';
				break;
		}
	}
	
	/**
	* XML Callback to process start elements. Elements currently 
	* processed are: QUERY. 
	*
	* @access private
	*/
	function _tag_open( &$parser, $tag, $attributes ) {
		$this->currentElement = strtoupper( $tag );
		
		switch( $this->currentElement ) {
			case 'QUERY':
				// Create a new query in a SQL queryset.
				// Ignore this query set if a platform is specified and it's different than the 
				// current connection platform.
				if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
					$this->newQuery();
				} else {
					$this->discardQuery();
				}
				break;
			default:
				// print_r( array( $tag, $attributes ) );
		}
	}
	
	/**
	* XML Callback to process CDATA elements
	*/
	function _tag_cdata( &$parser, $cdata ) {
		switch( $this->currentElement ) {
			// Line of queryset SQL data
			case 'QUERY':
				$this->buildQuery( $cdata );
				break;
			default:
				
		}
	}
	
	/**
	* XML Callback to process end elements
	*
	* @access private
	*/
	function _tag_close( &$parser, $tag ) {
		$this->currentElement = '';
		
		switch( strtoupper( $tag ) ) {
			case 'QUERY':
				// Add the finished query to the open query set.
				$this->addQuery();
				break;
			case 'SQL':
				$this->parent->addSQL( $this->create( $this->parent ) );
				xml_set_object( $parser, $this->parent );
				$this->destroy();
				break;
			default:
				
		}
	}
	
	/**
	* Re-initializes the query.
	*
	* @return boolean TRUE
	*/
	function newQuery() {
		$this->query = '';
		
		return TRUE;
	}
	
	/**
	* Discards the existing query.
	*
	* @return boolean TRUE
	*/
	function discardQuery() {
		unset( $this->query );
		
		return TRUE;
	}
	
	/** 
	* Appends a line to a query that is being built line by line
	*
	* @param string $data Line of SQL data or NULL to initialize a new query
	* @return string SQL query string.
	*/
	function buildQuery( $sql = NULL ) {
		if( !isset( $this->query ) OR empty( $sql ) ) {
			return FALSE;
		}
		
		$this->query .= $sql;
		
		return $this->query;
	}
	
	/**
	* Adds a completed query to the query list
	*
	* @return string	SQL of added query
	*/
	function addQuery() {
		if( !isset( $this->query ) ) {
			return FALSE;
		}
		
		$this->queries[] = $return = trim($this->query);
		
		unset( $this->query );
		
		return $return;
	}
	
	/**
	* Creates and returns the current query set
	*
	* @param object $xmls adoSchema object
	* @return array Query set
	*/
	function create( &$xmls ) {
		foreach( $this->queries as $id => $query ) {
			switch( $this->prefixMethod ) {
				case 'AUTO':
					// Enable auto prefix replacement
					
					// Process object prefix.
					// Evaluate SQL statements to prepend prefix to objects
					$query = $this->prefixQuery( '/^\s*((?is)INSERT\s+(INTO\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
					$query = $this->prefixQuery( '/^\s*((?is)UPDATE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
					$query = $this->prefixQuery( '/^\s*((?is)DELETE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
					
					// SELECT statements aren't working yet
					#$data = preg_replace( '/(?ias)(^\s*SELECT\s+.*\s+FROM)\s+(\W\s*,?\s*)+((?i)\s+WHERE.*$)/', "\1 $prefix\2 \3", $data );
					
				case 'MANUAL':
					// If prefixKey is set and has a value then we use it to override the default constant XMLS_PREFIX.
					// If prefixKey is not set, we use the default constant XMLS_PREFIX
					if( isset( $this->prefixKey ) AND( $this->prefixKey !== '' ) ) {
						// Enable prefix override
						$query = str_replace( $this->prefixKey, $xmls->objectPrefix, $query );
					} else {
						// Use default replacement
						$query = str_replace( XMLS_PREFIX , $xmls->objectPrefix, $query );
					}
			}
			
			$this->queries[$id] = trim( $query );
		}
		
		// Return the query set array
		return $this->queries;
	}
	
	/**
	* Rebuilds the query with the prefix attached to any objects
	*
	* @param string $regex Regex used to add prefix
	* @param string $query SQL query string
	* @param string $prefix Prefix to be appended to tables, indices, etc.
	* @return string Prefixed SQL query string.
	*/
	function prefixQuery( $regex, $query, $prefix = NULL ) {
		if( !isset( $prefix ) ) {
			return $query;
		}
		
		if( preg_match( $regex, $query, $match ) ) {
			$preamble = $match[1];
			$postamble = $match[5];
			$objectList = explode( ',', $match[3] );
			// $prefix = $prefix . '_';
			
			$prefixedList = '';
			
			foreach( $objectList as $object ) {
				if( $prefixedList !== '' ) {
					$prefixedList .= ', ';
				}
				
				$prefixedList .= $prefix . trim( $object );
			}
			
			$query = $preamble . ' ' . $prefixedList . ' ' . $postamble;
		}
		
		return $query;
	}
}

/**
* Loads and parses an XML file, creating an array of "ready-to-run" SQL statements
* 
* This class is used to load and parse the XML file, to create an array of SQL statements
* that can be used to build a database, and to build the database using the SQL array.
*
* @tutorial getting_started.pkg
*
* @author Richard Tango-Lowy & Dan Cech
* @version $Revision: 1.62 $
*
* @package axmls
*/
class adoSchema {
	
	/**
	* @var array	Array containing SQL queries to generate all objects
	* @access private
	*/
	var $sqlArray;
	
	/**
	* @var object	ADOdb connection object
	* @access private
	*/
	var $db;
	
	/**
	* @var object	ADOdb Data Dictionary
	* @access private
	*/
	var $dict;
	
	/**
	* @var string Current XML element
	* @access private
	*/
	var $currentElement = '';
	
	/**
	* @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database
	* @access private
	*/
	var $upgrade = '';
	
	/**
	* @var string Optional object prefix
	* @access private
	*/
	var $objectPrefix = '';
	
	/**
	* @var long	Original Magic Quotes Runtime value
	* @access private
	*/
	var $mgq;
	
	/**
	* @var long	System debug
	* @access private
	*/
	var $debug;
	
	/**
	* @var string Regular expression to find schema version
	* @access private
	*/
	var $versionRegex = '/<schema.*?( version="([^"]*)")?.*?>/';
	
	/**
	* @var string Current schema version
	* @access private
	*/
	var $schemaVersion;
	
	/**
	* @var int	Success of last Schema execution
	*/
	var $success;
	
	/**
	* @var bool	Execute SQL inline as it is generated
	*/
	var $executeInline;
	
	/**
	* @var bool	Continue SQL execution if errors occur
	*/
	var $continueOnError;
	
	/**
	* @var int	How to handle existing data rows (insert, update, or ignore)
	*/
	var $existingData;
	
	/**
	* Creates an adoSchema object
	*
	* Creating an adoSchema object is the first step in processing an XML schema.
	* The only parameter is an ADOdb database connection object, which must already
	* have been created.
	*
	* @param object $db ADOdb database connection object.
	*/
	function adoSchema( $db ) {
		// Initialize the environment
		$this->mgq = get_magic_quotes_runtime();
		#set_magic_quotes_runtime(0);
		ini_set("magic_quotes_runtime", 0);
		
		$this->db = $db;
		$this->debug = $this->db->debug;
		$this->dict = NewDataDictionary( $this->db );
		$this->sqlArray = array();
		$this->schemaVersion = XMLS_SCHEMA_VERSION;
		$this->executeInline( XMLS_EXECUTE_INLINE );
		$this->continueOnError( XMLS_CONTINUE_ON_ERROR );
		$this->existingData( XMLS_EXISTING_DATA );
		$this->setUpgradeMethod();
	}
	
	/**
	* Sets the method to be used for upgrading an existing database
	*
	* Use this method to specify how existing database objects should be upgraded.
	* The method option can be set to ALTER, REPLACE, BEST, or NONE. ALTER attempts to
	* alter each database object directly, REPLACE attempts to rebuild each object
	* from scratch, BEST attempts to determine the best upgrade method for each
	* object, and NONE disables upgrading.
	*
	* This method is not yet used by AXMLS, but exists for backward compatibility.
	* The ALTER method is automatically assumed when the adoSchema object is
	* instantiated; other upgrade methods are not currently supported.
	*
	* @param string $method Upgrade method (ALTER|REPLACE|BEST|NONE)
	* @returns string Upgrade method used
	*/
	function SetUpgradeMethod( $method = '' ) {
		if( !is_string( $method ) ) {
			return FALSE;
		}
		
		$method = strtoupper( $method );
		
		// Handle the upgrade methods
		switch( $method ) {
			case 'ALTER':
				$this->upgrade = $method;
				break;
			case 'REPLACE':
				$this->upgrade = $method;
				break;
			case 'BEST':
				$this->upgrade = 'ALTER';
				break;
			case 'NONE':
				$this->upgrade = 'NONE';
				break;
			default:
				// Use default if no legitimate method is passed.
				$this->upgrade = XMLS_DEFAULT_UPGRADE_METHOD;
		}
		
		return $this->upgrade;
	}
	
	/**
	* Specifies how to handle existing data row when there is a unique key conflict.
	*
	* The existingData setting specifies how the parser should handle existing rows
	* when a unique key violation occurs during the insert. This can happen when inserting
	* data into an existing table with one or more primary keys or unique indexes.
	* The existingData method takes one of three options: XMLS_MODE_INSERT attempts
	* to always insert the data as a new row. In the event of a unique key violation,
	* the database will generate an error.  XMLS_MODE_UPDATE attempts to update the 
	* any existing rows with the new data based upon primary or unique key fields in
	* the schema. If the data row in the schema specifies no unique fields, the row
	* data will be inserted as a new row. XMLS_MODE_IGNORE specifies that any data rows
	* that would result in a unique key violation be ignored; no inserts or updates will
	* take place. For backward compatibility, the default setting is XMLS_MODE_INSERT,
	* but XMLS_MODE_UPDATE will generally be the most appropriate setting.
	*
	* @param int $mode XMLS_MODE_INSERT, XMLS_MODE_UPDATE, or XMLS_MODE_IGNORE
	* @return int current mode
	*/
	function ExistingData( $mode = NULL ) {
		if( is_int( $mode ) ) {
			switch( $mode ) {
				case XMLS_MODE_UPDATE:
					$mode = XMLS_MODE_UPDATE;
					break;
				case XMLS_MODE_IGNORE:
					$mode = XMLS_MODE_IGNORE;
					break;
				case XMLS_MODE_INSERT:
					$mode = XMLS_MODE_INSERT;
					break;
				default:
					$mode = XMLS_EXISTING_DATA;
					break;
			}
			$this->existingData = $mode;
		}
		
		return $this->existingData;
	}
	
	/**
	* Enables/disables inline SQL execution.
	*
	* Call this method to enable or disable inline execution of the schema. If the mode is set to TRUE (inline execution),
	* AXMLS applies the SQL to the database immediately as each schema entity is parsed. If the mode
	* is set to FALSE (post execution), AXMLS parses the entire schema and you will need to call adoSchema::ExecuteSchema()
	* to apply the schema to the database.
	*
	* @param bool $mode execute
	* @return bool current execution mode
	*
	* @see ParseSchema(), ExecuteSchema()
	*/
	function ExecuteInline( $mode = NULL ) {
		if( is_bool( $mode ) ) {
			$this->executeInline = $mode;
		}
		
		return $this->executeInline;
	}
	
	/**
	* Enables/disables SQL continue on error.
	*
	* Call this method to enable or disable continuation of SQL execution if an error occurs.
	* If the mode is set to TRUE (continue), AXMLS will continue to apply SQL to the database, even if an error occurs.
	* If the mode is set to FALSE (halt), AXMLS will halt execution of generated sql if an error occurs, though parsing
	* of the schema will continue.
	*
	* @param bool $mode execute
	* @return bool current continueOnError mode
	*
	* @see addSQL(), ExecuteSchema()
	*/
	function ContinueOnError( $mode = NULL ) {
		if( is_bool( $mode ) ) {
			$this->continueOnError = $mode;
		}
		
		return $this->continueOnError;
	}
	
	/**
	* Loads an XML schema from a file and converts it to SQL.
	*
	* Call this method to load the specified schema (see the DTD for the proper format) from
	* the filesystem and generate the SQL necessary to create the database
	* described. This method automatically converts the schema to the latest
	* axmls schema version.
	* @see ParseSchemaString()
	*
	* @param string $file Name of XML schema file.
	* @param bool $returnSchema Return schema rather than parsing.
	* @return array Array of SQL queries, ready to execute
	*/
	function ParseSchema( $filename, $returnSchema = FALSE ) {
		return $this->ParseSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );
	}
	
	/**
	* Loads an XML schema from a file and converts it to SQL.
	*
	* Call this method to load the specified schema directly from a file (see
	* the DTD for the proper format) and generate the SQL necessary to create
	* the database described by the schema. Use this method when you are dealing
	* with large schema files. Otherwise, ParseSchema() is faster.
	* This method does not automatically convert the schema to the latest axmls
	* schema version. You must convert the schema manually using either the
	* ConvertSchemaFile() or ConvertSchemaString() method.
	* @see ParseSchema()
	* @see ConvertSchemaFile()
	* @see ConvertSchemaString()
	*
	* @param string $file Name of XML schema file.
	* @param bool $returnSchema Return schema rather than parsing.
	* @return array Array of SQL queries, ready to execute.
	*
	* @deprecated Replaced by adoSchema::ParseSchema() and adoSchema::ParseSchemaString()
	* @see ParseSchema(), ParseSchemaString()
	*/
	function ParseSchemaFile( $filename, $returnSchema = FALSE ) {
		// Open the file
		if( !($fp = fopen( $filename, 'r' )) ) {
			logMsg( 'Unable to open file' );
			return FALSE;
		}
		
		// do version detection here
		if( $this->SchemaFileVersion( $filename ) != $this->schemaVersion ) {
			logMsg( 'Invalid Schema Version' );
			return FALSE;
		}
		
		if( $returnSchema ) {
			$xmlstring = '';
			while( $data = fread( $fp, 4096 ) ) {
				$xmlstring .= $data . "\n";
			}
			return $xmlstring;
		}
		
		$this->success = 2;
		
		$xmlParser = $this->create_parser();
		
		// Process the file
		while( $data = fread( $fp, 4096 ) ) {
			if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) {
				die( sprintf(
					"XML error: %s at line %d",
					xml_error_string( xml_get_error_code( $xmlParser) ),
					xml_get_current_line_number( $xmlParser)
				) );
			}
		}
		
		xml_parser_free( $xmlParser );
		
		return $this->sqlArray;
	}
	
	/**
	* Converts an XML schema string to SQL.
	*
	* Call this method to parse a string containing an XML schema (see the DTD for the proper format)
	* and generate the SQL necessary to create the database described by the schema. 
	* @see ParseSchema()
	*
	* @param string $xmlstring XML schema string.
	* @param bool $returnSchema Return schema rather than parsing.
	* @return array Array of SQL queries, ready to execute.
	*/
	function ParseSchemaString( $xmlstring, $returnSchema = FALSE ) {
		if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {
			logMsg( 'Empty or Invalid Schema' );
			return FALSE;
		}
		
		// do version detection here
		if( $this->SchemaStringVersion( $xmlstring ) != $this->schemaVersion ) {
			logMsg( 'Invalid Schema Version' );
			return FALSE;
		}
		
		if( $returnSchema ) {
			return $xmlstring;
		}
		
		$this->success = 2;
		
		$xmlParser = $this->create_parser();
		
		if( !xml_parse( $xmlParser, $xmlstring, TRUE ) ) {
			die( sprintf(
				"XML error: %s at line %d",
				xml_error_string( xml_get_error_code( $xmlParser) ),
				xml_get_current_line_number( $xmlParser)
			) );
		}
		
		xml_parser_free( $xmlParser );
		
		return $this->sqlArray;
	}
	
	/**
	* Loads an XML schema from a file and converts it to uninstallation SQL.
	*
	* Call this method to load the specified schema (see the DTD for the proper format) from
	* the filesystem and generate the SQL necessary to remove the database described.
	* @see RemoveSchemaString()
	*
	* @param string $file Name of XML schema file.
	* @param bool $returnSchema Return schema rather than parsing.
	* @return array Array of SQL queries, ready to execute
	*/
	function RemoveSchema( $filename, $returnSchema = FALSE ) {
		return $this->RemoveSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );
	}
	
	/**
	* Converts an XML schema string to uninstallation SQL.
	*
	* Call this method to parse a string containing an XML schema (see the DTD for the proper format)
	* and generate the SQL necessary to uninstall the database described by the schema. 
	* @see RemoveSchema()
	*
	* @param string $schema XML schema string.
	* @param bool $returnSchema Return schema rather than parsing.
	* @return array Array of SQL queries, ready to execute.
	*/
	function RemoveSchemaString( $schema, $returnSchema = FALSE ) {
		
		// grab current version
		if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {
			return FALSE;
		}
		
		return $this->ParseSchemaString( $this->TransformSchema( $schema, 'remove-' . $version), $returnSchema );
	}
	
	/**
	* Applies the current XML schema to the database (post execution).
	*
	* Call this method to apply the current schema (generally created by calling 
	* ParseSchema() or ParseSchemaString() ) to the database (creating the tables, indexes, 
	* and executing other SQL specified in the schema) after parsing.
	* @see ParseSchema(), ParseSchemaString(), ExecuteInline()
	*
	* @param array $sqlArray Array of SQL statements that will be applied rather than
	*		the current schema.
	* @param boolean $continueOnErr Continue to apply the schema even if an error occurs.
	* @returns integer 0 if failure, 1 if errors, 2 if successful.
	*/
	function ExecuteSchema( $sqlArray = NULL, $continueOnErr =  NULL ) {
		if( !is_bool( $continueOnErr ) ) {
			$continueOnErr = $this->ContinueOnError();
		}
		
		if( !isset( $sqlArray ) ) {
			$sqlArray = $this->sqlArray;
		}
		
		if( !is_array( $sqlArray ) ) {
			$this->success = 0;
		} else {
			$this->success = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr );
		}
		
		return $this->success;
	}
	
	/**
	* Returns the current SQL array. 
	*
	* Call this method to fetch the array of SQL queries resulting from 
	* ParseSchema() or ParseSchemaString(). 
	*
	* @param string $format Format: HTML, TEXT, or NONE (PHP array)
	* @return array Array of SQL statements or FALSE if an error occurs
	*/
	function PrintSQL( $format = 'NONE' ) {
		$sqlArray = null;
		return $this->getSQL( $format, $sqlArray );
	}
	
	/**
	* Saves the current SQL array to the local filesystem as a list of SQL queries.
	*
	* Call this method to save the array of SQL queries (generally resulting from a
	* parsed XML schema) to the filesystem.
	*
	* @param string $filename Path and name where the file should be saved.
	* @return boolean TRUE if save is successful, else FALSE. 
	*/
	function SaveSQL( $filename = './schema.sql' ) {
		
		if( !isset( $sqlArray ) ) {
			$sqlArray = $this->sqlArray;
		}
		if( !isset( $sqlArray ) ) {
			return FALSE;
		}
		
		$fp = fopen( $filename, "w" );
		
		foreach( $sqlArray as $key => $query ) {
			fwrite( $fp, $query . ";\n" );
		}
		fclose( $fp );
	}
	
	/**
	* Create an xml parser
	*
	* @return object PHP XML parser object
	*
	* @access private
	*/
	function create_parser() {
		// Create the parser
		$xmlParser = xml_parser_create();
		xml_set_object( $xmlParser, $this );
		
		// Initialize the XML callback functions
		xml_set_element_handler( $xmlParser, '_tag_open', '_tag_close' );
		xml_set_character_data_handler( $xmlParser, '_tag_cdata' );
		
		return $xmlParser;
	}
	
	/**
	* XML Callback to process start elements
	*
	* @access private
	*/
	function _tag_open( &$parser, $tag, $attributes ) {
		switch( strtoupper( $tag ) ) {
			case 'TABLE':
				if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
				$this->obj = new dbTable( $this, $attributes );
				xml_set_object( $parser, $this->obj );
				}
				break;
			case 'SQL':
				if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
					$this->obj = new dbQuerySet( $this, $attributes );
					xml_set_object( $parser, $this->obj );
				}
				break;
			default:
				// print_r( array( $tag, $attributes ) );
		}
		
	}
	
	/**
	* XML Callback to process CDATA elements
	*
	* @access private
	*/
	function _tag_cdata( &$parser, $cdata ) {
	}
	
	/**
	* XML Callback to process end elements
	*
	* @access private
	* @internal
	*/
	function _tag_close( &$parser, $tag ) {
		
	}
	
	/**
	* Converts an XML schema string to the specified DTD version.
	*
	* Call this method to convert a string containing an XML schema to a different AXMLS
	* DTD version. For instance, to convert a schema created for an pre-1.0 version for 
	* AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version 
	* parameter is specified, the schema will be converted to the current DTD version. 
	* If the newFile parameter is provided, the converted schema will be written to the specified
	* file.
	* @see ConvertSchemaFile()
	*
	* @param string $schema String containing XML schema that will be converted.
	* @param string $newVersion DTD version to convert to.
	* @param string $newFile File name of (converted) output file.
	* @return string Converted XML schema or FALSE if an error occurs.
	*/
	function ConvertSchemaString( $schema, $newVersion = NULL, $newFile = NULL ) {
		
		// grab current version
		if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {
			return FALSE;
		}
		
		if( !isset ($newVersion) ) {
			$newVersion = $this->schemaVersion;
		}
		
		if( $version == $newVersion ) {
			$result = $schema;
		} else {
			$result = $this->TransformSchema( $schema, 'convert-' . $version . '-' . $newVersion);
		}
		
		if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {
			fwrite( $fp, $result );
			fclose( $fp );
		}
		
		return $result;
	}

	/*
	// compat for pre-4.3 - jlim
	function _file_get_contents($path)
	{
		if (function_exists('file_get_contents')) return file_get_contents($path);
		return join('',file($path));
	}*/
	
	/**
	* Converts an XML schema file to the specified DTD version.
	*
	* Call this method to convert the specified XML schema file to a different AXMLS
	* DTD version. For instance, to convert a schema created for an pre-1.0 version for 
	* AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version 
	* parameter is specified, the schema will be converted to the current DTD version. 
	* If the newFile parameter is provided, the converted schema will be written to the specified
	* file.
	* @see ConvertSchemaString()
	*
	* @param string $filename Name of XML schema file that will be converted.
	* @param string $newVersion DTD version to convert to.
	* @param string $newFile File name of (converted) output file.
	* @return string Converted XML schema or FALSE if an error occurs.
	*/
	function ConvertSchemaFile( $filename, $newVersion = NULL, $newFile = NULL ) {
		
		// grab current version
		if( !( $version = $this->SchemaFileVersion( $filename ) ) ) {
			return FALSE;
		}
		
		if( !isset ($newVersion) ) {
			$newVersion = $this->schemaVersion;
		}
		
		if( $version == $newVersion ) {
			$result = _file_get_contents( $filename );
			
			// remove unicode BOM if present
			if( substr( $result, 0, 3 ) == sprintf( '%c%c%c', 239, 187, 191 ) ) {
				$result = substr( $result, 3 );
			}
		} else {
			$result = $this->TransformSchema( $filename, 'convert-' . $version . '-' . $newVersion, 'file' );
		}
		
		if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {
			fwrite( $fp, $result );
			fclose( $fp );
		}
		
		return $result;
	}
	
	function TransformSchema( $schema, $xsl, $schematype='string' )
	{
		// Fail if XSLT extension is not available
		if( ! function_exists( 'xslt_create' ) ) {
			return FALSE;
		}
		
		$xsl_file = dirname( __FILE__ ) . '/xsl/' . $xsl . '.xsl';
		
		// look for xsl
		if( !is_readable( $xsl_file ) ) {
			return FALSE;
		}
		
		switch( $schematype )
		{
			case 'file':
				if( !is_readable( $schema ) ) {
					return FALSE;
				}
				
				$schema = _file_get_contents( $schema );
				break;
			case 'string':
			default:
				if( !is_string( $schema ) ) {
					return FALSE;
				}
		}
		
		$arguments = array (
			'/_xml' => $schema,
			'/_xsl' => _file_get_contents( $xsl_file )
		);
		
		// create an XSLT processor
		$xh = xslt_create ();
		
		// set error handler
		xslt_set_error_handler ($xh, array (&$this, 'xslt_error_handler'));
		
		// process the schema
		$result = xslt_process ($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $arguments); 
		
		xslt_free ($xh);
		
		return $result;
	}
	
	/**
	* Processes XSLT transformation errors
	*
	* @param object $parser XML parser object
	* @param integer $errno Error number
	* @param integer $level Error level
	* @param array $fields Error information fields
	*
	* @access private
	*/
	function xslt_error_handler( $parser, $errno, $level, $fields ) {
		if( is_array( $fields ) ) {
			$msg = array(
				'Message Type' => ucfirst( $fields['msgtype'] ),
				'Message Code' => $fields['code'],
				'Message' => $fields['msg'],
				'Error Number' => $errno,
				'Level' => $level
			);
			
			switch( $fields['URI'] ) {
				case 'arg:/_xml':
					$msg['Input'] = 'XML';
					break;
				case 'arg:/_xsl':
					$msg['Input'] = 'XSL';
					break;
				default:
					$msg['Input'] = $fields['URI'];
			}
			
			$msg['Line'] = $fields['line'];
		} else {
			$msg = array(
				'Message Type' => 'Error',
				'Error Number' => $errno,
				'Level' => $level,
				'Fields' => var_export( $fields, TRUE )
			);
		}
		
		$error_details = $msg['Message Type'] . ' in XSLT Transformation' . "\n"
					   . '<table>' . "\n";
		
		foreach( $msg as $label => $details ) {
			$error_details .= '<tr><td><b>' . $label . ': </b></td><td>' . htmlentities( $details ) . '</td></tr>' . "\n";
		}
		
		$error_details .= '</table>';
		
		trigger_error( $error_details, E_USER_ERROR );
	}
	
	/**
	* Returns the AXMLS Schema Version of the requested XML schema file.
	*
	* Call this method to obtain the AXMLS DTD version of the requested XML schema file.
	* @see SchemaStringVersion()
	*
	* @param string $filename AXMLS schema file
	* @return string Schema version number or FALSE on error
	*/
	function SchemaFileVersion( $filename ) {
		// Open the file
		if( !($fp = fopen( $filename, 'r' )) ) {
			// die( 'Unable to open file' );
			return FALSE;
		}
		
		// Process the file
		while( $data = fread( $fp, 4096 ) ) {
			if( preg_match( $this->versionRegex, $data, $matches ) ) {
				return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;
			}
		}
		
		return FALSE;
	}
	
	/**
	* Returns the AXMLS Schema Version of the provided XML schema string.
	*
	* Call this method to obtain the AXMLS DTD version of the provided XML schema string.
	* @see SchemaFileVersion()
	*
	* @param string $xmlstring XML schema string
	* @return string Schema version number or FALSE on error
	*/
	function SchemaStringVersion( $xmlstring ) {
		if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {
			return FALSE;
		}
		
		if( preg_match( $this->versionRegex, $xmlstring, $matches ) ) {
			return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;
		}
		
		return FALSE;
	}
	
	/**
	* Extracts an XML schema from an existing database.
	*
	* Call this method to create an XML schema string from an existing database.
	* If the data parameter is set to TRUE, AXMLS will include the data from the database
	* in the schema. 
	*
	* @param boolean $data Include data in schema dump
	* @indent string indentation to use
	* @prefix string extract only tables with given prefix
	* @stripprefix strip prefix string when storing in XML schema
	* @return string Generated XML schema
	*/
	function ExtractSchema( $data = FALSE, $indent = '  ', $prefix = '' , $stripprefix=false) {
		$old_mode = $this->db->SetFetchMode( ADODB_FETCH_NUM );
		
		$schema = '<?xml version="1.0"?>' . "\n"
				. '<schema version="' . $this->schemaVersion . '">' . "\n";
		
		if( is_array( $tables = $this->db->MetaTables( 'TABLES' , ($prefix) ? $prefix.'%' : '') ) ) {
			foreach( $tables as $table ) {
				if ($stripprefix) $table = str_replace(str_replace('\\_', '_', $pfx ), '', $table);
				$schema .= $indent . '<table name="' . htmlentities( $table ) . '">' . "\n";
				
				// grab details from database
				$rs = $this->db->Execute( 'SELECT * FROM ' . $table . ' WHERE -1' );
				$fields = $this->db->MetaColumns( $table );
				$indexes = $this->db->MetaIndexes( $table );
				
				if( is_array( $fields ) ) {
					foreach( $fields as $details ) {
						$extra = '';
						$content = array();
						
						if( isset($details->max_length) && $details->max_length > 0 ) {
							$extra .= ' size="' . $details->max_length . '"';
						}
						
						if( isset($details->primary_key) && $details->primary_key ) {
							$content[] = '<KEY/>';
						} elseif( isset($details->not_null) && $details->not_null ) {
							$content[] = '<NOTNULL/>';
						}
						
						if( isset($details->has_default) && $details->has_default ) {
							$content[] = '<DEFAULT value="' . htmlentities( $details->default_value ) . '"/>';
						}
						
						if( isset($details->auto_increment) && $details->auto_increment ) {
							$content[] = '<AUTOINCREMENT/>';
						}
						
						if( isset($details->unsigned) && $details->unsigned ) {
							$content[] = '<UNSIGNED/>';
						}
						
						// this stops the creation of 'R' columns,
						// AUTOINCREMENT is used to create auto columns
						$details->primary_key = 0;
						$type = $rs->MetaType( $details );
						
						$schema .= str_repeat( $indent, 2 ) . '<field name="' . htmlentities( $details->name ) . '" type="' . $type . '"' . $extra;
						
						if( !empty( $content ) ) {
							$schema .= ">\n" . str_repeat( $indent, 3 )
									 . implode( "\n" . str_repeat( $indent, 3 ), $content ) . "\n"
									 . str_repeat( $indent, 2 ) . '</field>' . "\n";
						} else {
							$schema .= "/>\n";
						}
					}
				}
				
				if( is_array( $indexes ) ) {
					foreach( $indexes as $index => $details ) {
						$schema .= str_repeat( $indent, 2 ) . '<index name="' . $index . '">' . "\n";
						
						if( $details['unique'] ) {
							$schema .= str_repeat( $indent, 3 ) . '<UNIQUE/>' . "\n";
						}
						
						foreach( $details['columns'] as $column ) {
							$schema .= str_repeat( $indent, 3 ) . '<col>' . htmlentities( $column ) . '</col>' . "\n";
						}
						
						$schema .= str_repeat( $indent, 2 ) . '</index>' . "\n";
					}
				}
				
				if( $data ) {
					$rs = $this->db->Execute( 'SELECT * FROM ' . $table );
					
					if( is_object( $rs ) && !$rs->EOF ) {
						$schema .= str_repeat( $indent, 2 ) . "<data>\n";
						
						while( $row = $rs->FetchRow() ) {
							foreach( $row as $key => $val ) {
								if ( $val != htmlentities( $val ) ) {
									$row[$key] = '<![CDATA[' . $val . ']]>';
								}
							}
							
							$schema .= str_repeat( $indent, 3 ) . '<row><f>' . implode( '</f><f>', $row ) . "</f></row>\n";
						}
						
						$schema .= str_repeat( $indent, 2 ) . "</data>\n";
					}
				}
				
				$schema .= $indent . "</table>\n";
			}
		}
		
		$this->db->SetFetchMode( $old_mode );
		
		$schema .= '</schema>';
		return $schema;
	}
	
	/**
	* Sets a prefix for database objects
	*
	* Call this method to set a standard prefix that will be prepended to all database tables 
	* and indices when the schema is parsed. Calling setPrefix with no arguments clears the prefix.
	*
	* @param string $prefix Prefix that will be prepended.
	* @param boolean $underscore If TRUE, automatically append an underscore character to the prefix.
	* @return boolean TRUE if successful, else FALSE
	*/
	function SetPrefix( $prefix = '', $underscore = TRUE ) {
		switch( TRUE ) {
			// clear prefix
			case empty( $prefix ):
				logMsg( 'Cleared prefix' );
				$this->objectPrefix = '';
				return TRUE;
			// prefix too long
			case strlen( $prefix ) > XMLS_PREFIX_MAXLEN:
			// prefix contains invalid characters
			case !preg_match( '/^[a-z][a-z0-9_]+$/i', $prefix ):
				logMsg( 'Invalid prefix: ' . $prefix );
				return FALSE;
		}
		
		if( $underscore AND substr( $prefix, -1 ) != '_' ) {
			$prefix .= '_';
		}
		
		// prefix valid
		logMsg( 'Set prefix: ' . $prefix );
		$this->objectPrefix = $prefix;
		return TRUE;
	}
	
	/**
	* Returns an object name with the current prefix prepended.
	*
	* @param string	$name Name
	* @return string	Prefixed name
	*
	* @access private
	*/
	function prefix( $name = '' ) {
		// if prefix is set
		if( !empty( $this->objectPrefix ) ) {
			// Prepend the object prefix to the table name
			// prepend after quote if used
			return preg_replace( '/^(`?)(.+)$/', '$1' . $this->objectPrefix . '$2', $name );
		}
		
		// No prefix set. Use name provided.
		return $name;
	}
	
	/**
	* Checks if element references a specific platform
	*
	* @param string $platform Requested platform
	* @returns boolean TRUE if platform check succeeds
	*
	* @access private
	*/
	function supportedPlatform( $platform = NULL ) {
		if( !empty( $platform ) ) {
			$regex = '/(^|\|)' . $this->db->databaseType . '(\||$)/i';
		
			if( preg_match( '/^- /', $platform ) ) {
				if (preg_match ( $regex, substr( $platform, 2 ) ) ) {
					logMsg( 'Platform ' . $platform . ' is NOT supported' );
					return FALSE;
				}
		} else {
				if( !preg_match ( $regex, $platform ) ) {
					logMsg( 'Platform ' . $platform . ' is NOT supported' );
			return FALSE;
		}
	}
		}
		
		logMsg( 'Platform ' . $platform . ' is supported' );
		return TRUE;
	}
	
	/**
	* Clears the array of generated SQL.
	*
	* @access private
	*/
	function clearSQL() {
		$this->sqlArray = array();
	}
	
	/**
	* Adds SQL into the SQL array.
	*
	* @param mixed $sql SQL to Add
	* @return boolean TRUE if successful, else FALSE.
	*
	* @access private
	*/	
	function addSQL( $sql = NULL ) {
		if( is_array( $sql ) ) {
			foreach( $sql as $line ) {
				$this->addSQL( $line );
			}
			
			return TRUE;
		}
		
		if( is_string( $sql ) ) {
			$this->sqlArray[] = $sql;
			
			// if executeInline is enabled, and either no errors have occurred or continueOnError is enabled, execute SQL.
			if( $this->ExecuteInline() && ( $this->success == 2 || $this->ContinueOnError() ) ) {
				$saved = $this->db->debug;
				$this->db->debug = $this->debug;
				$ok = $this->db->Execute( $sql );
				$this->db->debug = $saved;
				
				if( !$ok ) {
					if( $this->debug ) {
						ADOConnection::outp( $this->db->ErrorMsg() );
					}
					
					$this->success = 1;
				}
			}
			
			return TRUE;
		}
		
		return FALSE;
	}
	
	/**
	* Gets the SQL array in the specified format.
	*
	* @param string $format Format
	* @return mixed SQL
	*	
	* @access private
	*/
	function getSQL( $format = NULL, $sqlArray = NULL ) {
		if( !is_array( $sqlArray ) ) {
			$sqlArray = $this->sqlArray;
		}
		
		if( !is_array( $sqlArray ) ) {
			return FALSE;
		}
		
		switch( strtolower( $format ) ) {
			case 'string':
			case 'text':
				return !empty( $sqlArray ) ? implode( ";\n\n", $sqlArray ) . ';' : '';
			case'html':
				return !empty( $sqlArray ) ? nl2br( htmlentities( implode( ";\n\n", $sqlArray ) . ';' ) ) : '';
		}
		
		return $this->sqlArray;
	}
	
	/**
	* Destroys an adoSchema object.
	*
	* Call this method to clean up after an adoSchema object that is no longer in use.
	* @deprecated adoSchema now cleans up automatically.
	*/
	function Destroy() {
		ini_set("magic_quotes_runtime", $this->mgq );
		#set_magic_quotes_runtime( $this->mgq );
		unset( $this );
	}
}

/**
* Message logging function
*
* @access private
*/
function logMsg( $msg, $title = NULL, $force = FALSE ) {
	if( XMLS_DEBUG or $force ) {
		echo '<pre>';
		
		if( isset( $title ) ) {
			echo '<h3>' . htmlentities( $title ) . '</h3>';
		}
		
		if( @is_object( $this ) ) {
			echo '[' . get_class( $this ) . '] ';
		}
		
		print_r( $msg );
		
		echo '</pre>';
	}
}
?>

Youez - 2016 - github.com/yon3zu
LinuXploit