      1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80  81  82  83  84  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398

    <?php
    namespace Catapult;
    /**
     * @class BaMLResource
     *
     * defines the base for BaML
     * generation  and parsing.
     * Uses the PHP internal SAX parser
     * for XML.
     *
     */

    namespace Catapult;

    abstract class BaMLResource {
        public static $options = array(
          BAML_XML_OPTIONS::BAML_XML_ENCODING => "UTF-8"
        );
        /** primary parser **/
        public $parser;
        public $fileHandler;

        /** primary data **/
        public $data;

        /** parsing tree variables **/
        /** what level what the element found on **/
        private $level = 0;
        /**
         * Generate the parser object
         * and tie to xml_set_object
         *
         * @param data: xml string when set use parsing otherwise treat as creation
         */
        public function __construct($data = null) {
          $this->parser = xml_parser_create();
          xml_set_object($this->parser, $this);
        }
        /**
         * valid will check if this is valid
         * according to BaML
         *
         * @param valid: BaML xml struct item with level, name and attributes
         */
        public function register($element, $container=false) {
          if (in_array($element['tag'], array("Request", "Response")))
            return new BaMLContainer($element['tag']);
          if (!(in_array($element['tag'], BaMLVerb::$valid)))
            throw new \CatapultApiException($element['tag'] . " is not a valid verb..");
          if ($container) {
            $class = "Catapult\\" . "BaMLVerb" . $element['tag'];
          } else {
            $class =  "Catapult\\" . "BaMLVerb" . $element['tag'];
          }
          $class = new $class;
          if (isset($element['attributes'])) {
             foreach ($element['attributes'] as $k => $attr) {
               $class->addAttribute($k, $attr);
             }
          }
          if (isset($element['value']))
            $class->setText(trim($element['value']));
          $class->level = $element['level'];
          return $class;
        }
        /**
         * parse the provided text.
         * generate a parser here
         * we usually wouldnt need to
         * have one initialized with the
         * object.
         *
         * @param text: valid BaML markup
         */
        public function parse($text) {
          $this->queue = array();
          $this->parser = xml_parser_create();
          $this->parsed = 0;
          $this->opened = 0;
          xml_set_object($this->parser, $this);
          xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
          xml_parser_set_option($this->parser, XML_OPTION_SKIP_WHITE, 1);
          $code = xml_parse_into_struct($this->parser, $text, $vals, $index);
          /**
           * Parse into bAML
           * type structure
           *
           */
          if ($code != 0) {
            return XMLUtility::Make($this, $vals);
          }

          throw new \CatapultApiException("Provided text was not valid XMLUtility..");
        }
        /**
         * reads a file, transferings
         * into a BaML object
         *
         * @param file: absolute or relative filepath
         */
        public function getAsStream($file) {
          $this->parse(FileHandler::read($file));
        }
        /**
         * add data
         * to an existing
         * BaML object
         *
         * @param data: BaMLAttribute or BaMLText or BaMLVerb
         */
        public function addData($object) {
          $this->data[] = $object;
        }
        /**
         * stub. goto __toString
         *
         */
        public function generate() {
          return $this->__toString();
        }
        /**
         * get the contents
         * for this baml
         * object
         */
        public function get() {
          return $this->data;
        }
        /**
         * Things to consider when
         * generating toString
         * parameters will always be
         * located as a member of this instance
         *
         * parameters should be in
         * xml mode and according to
         * schema either
         * wrap in Response|Request
         */
        public function __toString() {
          $str = "";
          $start = 1;
          $str .= XMLUtility::getHeader();
          $container = $this->data[0];
          /** root element is not a container, add **/
          if (!($container instanceof BaMLContainer)) {
              $container = new BaMLContainer;
              $start = 0;
          }
          $container->verbs = array_merge($container->verbs, array_slice($this->data, $start, sizeof($this->data)));
          $str .= XMLUtility::openTag($container->getType());
          /**
           * if the structure is
           * flat we need to take the remaining
           * pieces and merge with the container
           */
          $this->data = $container->verbs;
          foreach ($this->data as $bamlObj) {
            if ($bamlObj instanceof BaMLVerb) {
             $str .= XMLUtility::openTag($bamlObj->getName(), $bamlObj->getAttributes());
             $str .= XMLUtility::joinTree($bamlObj->getVerbs());
             $str .= (string) $bamlObj->getText();
             $str .= XMLUtility::closeTag($bamlObj->getName());
            }
          }
          $str .= XMLUtility::closeTag($container->getType());
          return ($str);
        }
    }
    /**
     * @class BaML
     *
     * main class to access BaML
     * features should be extended
     * by each verb and provide logic
     *
     */
    final class BaML extends BaMLResource {
        public $parameters;
        /** keep track of BaML sequentialy **/
        public function __construct($type='Response') {
          parent::__construct();
          $this->generateData($type);
          $this->generateParameters();
        }
        public function generateParameters() {

          $this->parameters = new Parameters;
        }
        /**
         * initial add
         * of container to data
         */
        public function generateData($type) {
          $this->addData(new BaMLContainer($type));
        }
        /**
         * do we have a verb?
         *
         */
        public function hasCurrentVerb() {
          if ($this->currentVerb == NULL)
            return False;
          return true;
        }
        /**
         *
         * public API method. Get the data
         * in the BaML container
         */
        public function get() {
          return $this->data;
        }
        /**
         * get all the verbs in this
         * check if its a flat array or
         * has multiple layers
         */
        public function getVerbs() {
          if (sizeof($this->data) > 1) {
            return array_merge($this->data[0]->verbs, array_slice($this->data, 1, sizeof($this->data)));
          }
          return $this->data[0]->verbs;
        }
        /**
         * parse a line
         * and add to document
         * @param line
         */
        public function addLine($line) {
          $this->trySet(XMLUtility::parse($line));
        }
        /**
         * get the current
         * verb.
         *
         */
        public function getCurrentVerb() {
          return $this->currentVerb;
        }
        /**
         * get the current
         * text
         *
         */
        public function getCurrentText() {
          $this->currentText = $object;
        }
        /**
         * set current verb
         *
         * @param object
         */
        public function setCurrentVerb(&$object) {
          $this->currentVerb = $object;
        }
        /**
         * set the current text
         * for baml
         */
        public function setCurrentText(&$object) {
          $this->currentText = $object;
        }
        /**
         * handle attributes
         *
         */
        private function handleAttributes($object) {
          if (isset($object['attributes']) && sizeof($object['attributes']) > 0) {
            foreach ($object['attributes'] as $val => $attr) {
              $this->addData(new BaMLAttribute($val, $attr));
            }
          } else {
              /** warn on no attributes no throw **/
            return new \CatapultApiWarning("Attributes were not provided for " . get_class($object));
          }
        }
        /**
         * handle an object
         * provided from baml's add funtion
         *
         * @param object
         * @param
         */
        private function handleObject($object, $type=null) {
            if (is_object($object)) {
              if (!($object instanceof BaMLAttribute || $object instanceof BaMLText || $object instanceof BaMLVerb)) {
                throw new \CatapultApiException("Not a valid object in BaML schema. Must be either BaMLAttribute, BaMLVerb or BaMLVerb");
              }
              if (!isset($this->currentVerb)) {
                $this->setCurrentVerb($object);
              }
              if (!isset($this->currentText)) {
                $this->setCurrentText($object);
              }

              $this->addData($object);
            } else {
                /** for this initialization we need type to be set **/
              $this->addData($object);
            }
        }
        /**
         * sets a verb. Generate
         * a new class from BaMLVerb
         * if we have a current verb
         * we cannot add a new one
         */
        public function setVerb($params) {
          return $this->handleObject($params);
        }
        public function setText($params) {
          if (!($this->hasCurrentVerb())) {
            throw new \CatapultApiException("Verbs must always be set before other texts..");
          }
          return $this->handleObject($params);
        }
        /**
         *
         * for setting an attribute
         * you can also use the text
         * as the second parameter
         */
        public function setAttribute($params) {
          if (!($this->hasCurrentVerb())) {
            throw new \CatapultApiException("Verbs must always be set before other attributes..");
          }
          if (!($this->hasCurrentText())) {
            throw new \CatapultApiException("Texts must always be set before other attributes..");
          }
          return $this->handleObject($params);
        }
        /**
         * set the container's
         * type.
         * it has to be in the
         * first element of
         * data
         * @param type: string (Request|Response)
         *
         */
        public function setContainerType($type) {
          $this->data[0]->setType($type);
        }
        /**
         * try setting an object
         * in the namespace
         * if we cant throw
         */
        public function trySet($object) {
          /** if we're passed one argument pluck its object **/
          if (sizeof($object) == 1)
            $object = $object[0];
           if (is_array($object)) {
             /** either its a verb, text or attribute **/
             /** we need to find type by its name **/

             /** create the object and any additional attributes **/
             /** attributes are set in "attributes" field of array **/
             $this->handleAttributes($object);
           } elseif (is_object($object)) {

            /** object based initialization **/
            $this->setVerb($object);
           } elseif (is_string($object)) {
              /** polymorphic style initialization **/
              /** first parameter needs to be the name of the instance **/
              /** following are attributes **/
            $args = array_slice($object[1], $object[sizeof($object) - 1]);

           } else {
              throw new \CatapultApiException("Invalid input for baML must be either string, instance or associative array");
           }
        }

        /** call the handler as a parameter, always consider the verb **/
        /** deal with setVerb, and setText differently **/
        /** attributes, and texts should always have the verb set first **/
        /**
         * do this by verifying class currentVerb is always
         * loaded first
         */

        public function __call($function, $args) {
          /**
           * if we only have a set
           * parameter try to take
           * by instance. like
           * $bamlobject->set(new Catapult\BaMLVerbSpeakSentence);
           */
          if ($function == "add" || $function == "set") {
              return $this->trySet($args);
          }
          /** branch to attribute setting here **/
          $this->parameters->__call($function, $args);
        }
    }