<?php
// $Revision: 1.1.4.1.2.21 $
// ------------------------------------------------------------------------- //
//  XooNIps - Neuroinformatics Base Platform System                          //
//  Copyright (C) 2005-2011 RIKEN, Japan All rights reserved.                //
//  http://xoonips.sourceforge.jp/                                           //
// ------------------------------------------------------------------------- //
//  This program is free software; you can redistribute it and/or modify     //
//  it under the terms of the GNU General Public License as published by     //
//  the Free Software Foundation; either version 2 of the License, or        //
//  (at your option) any later version.                                      //
//                                                                           //
//  You may not change or alter any portion of this comment or credits       //
//  of supporting developers from this source code or any supporting         //
//  source code which is considered copyrighted (c) material of the          //
//  original comment or credit authors.                                      //
//                                                                           //
//  This program is distributed in the hope that it will be useful,          //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of           //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            //
//  GNU General Public License for more details.                             //
//                                                                           //
//  You should have received a copy of the GNU General Public License        //
//  along with this program; if not, write to the Free Software              //
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA //
// ------------------------------------------------------------------------- //

include_once XOOPS_ROOT_PATH . '/modules/xoonips/class/xoonips_compo_item.class.php';

/**
 *
 * @brief Class that transform associative array of XML-RPC argument(parsed by XoopsXmlRpcParser) to DataObject
 *
 *
 */
class XooNIpsXmlRpcTransformElement
{

    /**
     * @brief add XooNIpsXmlRpcTransformElement to transform
     *
     * @param string $field field name correspond to $child
     * @param XooNIpsXmlRpcTransformElement $child child element
     * @return XooNIpsXmlRpcTransformEelemnt $child object to transform array
     */
    function add($field, &$child) 
    {
        if (isset($field) && !empty($field) && isset($child) && (is_subclass_of($child, 'XooNIpsXmlRpcTransformElement') || get_class($child) == 'XooNIpsXmlRpcTransformElement')) {
            $this->childs[$field] = $child;
        }
    }
}

/**
 *
 * @brief XmlRpcTransform composer class
 *
 * XmlRpcTransformCompo is composed by one or more XmlRpcTransform.
 * It has getObject method.
 *
 */
class XooNIpsXmlRpcTransformCompo extends XooNIpsXmlRpcTransformElement
{
    //    var $module = null;
    //    var $name = null;
    var $handlers = null;
    var $iteminfo = null;

    /**
     *
     * @param string $itemtype itemtype name to create in getObject
     *
     */
    function XooNIpsXmlRpcTransformCompo($module = null) 
    {
        if (isset($module)) $this->__init($module);
    }

    /**
     * @brief initialize module and name for xoonips_getormcompohandler
     * These parameter is used in getObject to create object to be returned.
     *
     * @param string $module
     */
    function __init($module) 
    {
        //        $this->module = $module;
        //        $this->name = $name;
        if (is_null($this->iteminfo)) {
            include XOOPS_ROOT_PATH . '/modules/' . $module . '/iteminfo.php';
            $this->iteminfo = &$iteminfo;
        }
        //
        // create and add orm accrding to $this -> iteminfo['orm']
        foreach($this->iteminfo['orm'] as $orm) {
            $this->handlers[$orm['field']] =& xoonips_getormhandler($orm['module'], $orm['name']);
        }
    }

    /**
     * @brief check that required fields are filled.
     * empty string('') is seems 'not filled'. Multiple variable must have at least one non-empty value.
     *
     * @param[in] $in_array associative array of item
     * @param[out] $missing array of string of missing field name
     * @retval ture filled
     * @retval false not filled
     */
    function isFilledRequired($in_array, &$missing) 
    {
        if (!isset($missing) || !is_array($missing)) $missing = array();
        foreach($this->iteminfo['io']['xmlrpc']['item'] as $input) {
            if (!isset($input['xmlrpc']['required']) || !$input['xmlrpc']['required']) continue; // no need to check required
            $value = null; //target value
            $name = null; //name of variable
            $is_multiple = isset($input['xmlrpc']['multiple']) ? $input['xmlrpc']['multiple'] : false;
            if ($input['xmlrpc']['field'][0] == 'detail_field') { // case of detail_field
                $name = implode('.', $input['xmlrpc']['field']);
                if( !empty($in_array['detail_field']) ){
                    foreach($in_array['detail_field'] as $field) {
                        if (trim($field['name']) == $input['xmlrpc']['field'][1]) {
                            $value = trim($field['value']);
                            break;
                        }
                    }
                }
            } else { // case of not detail_field
                $name = $input['xmlrpc']['field'][0];
                if (isset($in_array[$name])) $value = $in_array[$name];
            }
            //if( is_null( $value ) || is_null( $name ) ) continue;
            if ($is_multiple) {
                if (count($value) == 0) {
                    // missing if multiple variable has no values
                    $missing[] = $name;
                } else {
                    // set missing if empty value is found
                    foreach($value as $val) {
                        if (!empty($val)) break;
                        $missing[] = $name;
                        break;
                    }
                }
            } else if (empty($value)) {
                $missing[] = $name;
            }
        }
        return count($missing) == 0;
    }

    /**
     * @brief check that multiple fields has array and non-multiple field has single value
     * don't check a variable if its value is not specified.
     * 
     * @param[in] $in_array associative array of item
     * @param[out] $fields array of string of field name
     * @retval ture filled
     * @retval false not filled
     */
    function checkMultipleFields($in_array, &$fields) 
    {
        if (!isset($fields) || !is_array($fields)) $fields = array();
        foreach($this->iteminfo['io']['xmlrpc']['item'] as $input) {
            $value = null; //target value
            $name = null; //name of variable
            $is_multiple = isset($input['xmlrpc']['multiple']) ? $input['xmlrpc']['multiple'] : false;
            if ($input['xmlrpc']['field'][0] == 'detail_field') { // case of detail_field
                if( $is_multiple ) $value = array();
                $name = implode('.', $input['xmlrpc']['field']);
                foreach($in_array['detail_field'] as $field) {
                    if (trim($field['name']) == $input['xmlrpc']['field'][1]) {
                        if( isset( $value ) && !is_array( $value ) ) $value = array( $value );
                        if( is_array( $value ) ) $value[] = $field['value'];
                        else                     $value = $field['value'];
                    }
                }
            } else { // case of not detail_field
                $name = $input['xmlrpc']['field'][0];
                if (isset($in_array[$name])) $value = $in_array[$name];
            }
            // skip variable if it is not specified a value
            if( !isset( $value ) ) continue;
            //
            if ($is_multiple) {
                if (!is_array($value)) {
                    // error if multiple variable doesn't have an array
                    $fields[] = $name;
                }
            } else if (is_array($value)) {
                // error if non-multiple variable has an array
                $fields[] = $name;
            }
        }
        return count($fields) == 0;
    }
    
    /**
     * @brief check that each field has valid value.
     * 
     * @param[in] $in_array associative array of item
     * @param[out] $error XooNIpsError to add error
     * @retval ture valid
     * @retval false some invalid fields
     */
    function checkFields($in_array, &$error){
        $is_valid = true;
        foreach($this->iteminfo['io']['xmlrpc']['item'] as $input) {
            if ( !isset( $input['xmlrpc']['options'] ) ){
                continue;
            }
            $value = null; //target value
            $name = null; //name of variable
            $is_multiple = isset($input['xmlrpc']['multiple']) ? $input['xmlrpc']['multiple'] : false;
            if ($input['xmlrpc']['field'][0] == 'detail_field') { // case of detail_field
                if( $is_multiple ) $value = array();
                $name = implode('.', $input['xmlrpc']['field']);
                foreach($in_array['detail_field'] as $field) {
                    if (trim($field['name']) == $input['xmlrpc']['field'][1]) {
                        if( isset( $value ) && !is_array( $value ) ) $value = array( $value );
                        if( is_array( $value ) ) $value[] = $field['value'];
                        else                     $value = $field['value'];
                    }
                }
            } else { // case of not detail_field
                $name = $input['xmlrpc']['field'][0];
                if (isset($in_array[$name])) $value = $in_array[$name];
            }
            
            $valid_option_values = array();
            foreach ( $input['xmlrpc']['options'] as $option ){
                $valid_option_values[] = $option['option'];
            }
            
            if ($is_multiple) {
                // set missing if empty value is found
                foreach($value as $val) {
                    if ( !in_array( $val, $valid_option_values ) ){
                        $error->add(XNPERR_INVALID_PARAM, $name);
                        $is_valid = false;
                        break;
                    }
                }
            } else if (!in_array( $value, $valid_option_values )) {
                $error->add(XNPERR_INVALID_PARAM, $name);
                $is_valid = false;
            }
        }
        return $is_valid;
    }
    
    /**
     *
     * get orm composer object( subclass of XooNIpsItemCompo ) from associative array
     * see also {@link $this -> iteminfo}
     *
     * @param array associative array of XML-RPC argument
     * @return XooNIpsTableObject reference of subclass of XooNIpsTableObject
     */
    function getObject($in_array) 
    {
        $handler =& xoonips_getormcompohandler($this->iteminfo['ormcompo']['module'], $this->iteminfo['ormcompo']['name']);
        $this->iteminfo = $handler->getIteminfo();
        //
        // get primary key
        $primary_id = false;
        foreach($this->iteminfo['io']['xmlrpc']['item'] as $input) {
            if ($input['orm']['field'][0]['orm'] == $this->iteminfo['ormcompo']['primary_orm'] && $input['orm']['field'][0]['field'] == $this->iteminfo['ormcompo']['primary_key']) {
                $primary_id = isset( $in_array[ $input['xmlrpc']['field'][0] ] ) ? $in_array[ $input['xmlrpc']['field'][0] ] : false;
                break;
            }
        }
        if (false == $primary_id) {
            // create ormcompo
            $obj = $handler->create();
        } else {
            // get object from database
            $obj = $handler->get($primary_id); //var_dump('obj=get(primary_id):', $obj);
            if (!$obj) $obj = $handler->create();
        }
        $primary_orm = $obj->getVar($this->iteminfo['ormcompo']['primary_orm']);
        //
        // apply all input rule in iteminfo
        $unicode =& xoonips_getutility( 'unicode' );
        foreach($this->iteminfo['io']['xmlrpc']['item'] as $input) {
            $in_field = null;
            $in_var = array();
            $out_var = array();
            //
            // get reference of orm's information corresnponds to $input
            $orminfo = null;
            foreach($this->iteminfo['orm'] as $orm) {
                if ($orm['field'] == $input['orm']['field'][0]['orm']) {
                    $orminfo = $orm;
                    break;
                }
            }
            //
            // get variable in $in_var according to $this -> iteminfo['input'] rule
            $is_multiple = isset($input['xmlrpc']['multiple']) ? $input['xmlrpc']['multiple'] : false;
            if ($input['xmlrpc']['field'][0] == 'detail_field') {
                if( $is_multiple ) $in_field = array();
                foreach($in_array['detail_field'] as $field) {
                    if (trim($field['name']) == $input['xmlrpc']['field'][1]) {
                        if (isset($in_field) && !is_array($in_field)) $in_field = array(
                            $in_field
                        );
                        if (is_array($in_field)) $in_field[] = $field['value'];
                        else $in_field = trim($field['value']);
                    }
                }
            } else {
                $in_field = $in_array;
                foreach($input['xmlrpc']['field'] as $field) {
                    if (array_key_exists($field, $in_field)) {
                        $in_field = $in_field[$field];
                    } else {
                        // set empty array if $field is not given in $in_array
                        $in_field = array();
                        break;
                    }
                }
            }
            //
            // set value to orm variable
            if ($orminfo['multiple']) {
                //
                // get handler of orm to set variable
                $handler =& $this->handlers[$input['orm']['field'][0]['orm']];
                
                // prepare orm array
                $foreign_key = $orminfo['foreign_key'];
                $cri = new CriteriaCompo(new Criteria($foreign_key, $primary_orm->get($this->iteminfo['ormcompo']['primary_key'])));
                $var_objs =& $handler->getObjects($cri);
                array_splice($var_objs, count($in_field));//delete redundant element
                while(count($var_objs)<count($in_field)) {
                    $var_objs[] = $handler->create();
                }
                
                //
                $pos = 0;
                $array = array();
                foreach($in_field as $v) {
                    if (isset($array[$pos])) $var_obj = $array[$pos];
                    else $var_obj = $handler->create();
                    
                    // convert to numeric reference
                    if( is_string( $v ) ){
                        $in = $v;
                        $v = $unicode->decode_utf8($v,xoonips_get_server_charset(),'h');
                    }
                    
                    // evaluate
                    $in_var = array(
                        $v
                    );
                    $out_var = array();
                    $context = array(
                        'position' => $pos
                    );
                    eval(isset($input['eval']['xmlrpc2orm']) ? $input['eval']['xmlrpc2orm'] : '$out_var[0] = $in_var[0];');

                    //
                    for ($i = 0; $i < count($input['orm']['field']); $i++) {
                        if (isset($out_var[$i])){
                            $var_objs[$pos]->setVar($input['orm']['field'][$i]['field'], $out_var[$i], true);
                        }else{
                            $var_objs[$pos]->setDefault($input['orm']['field'][$i]['field']);
                        }
                    }
                    $array[$pos] = $var_objs[$pos];
                    $pos++;
                }
                $obj->setVar($input['orm']['field'][0]['orm'], $array);
            } else {
                // convert to numeric reference
                if( is_string( $in_field ) ){
                    $in_field = $unicode->decode_utf8(
                        $in_field,xoonips_get_server_charset(),'h');
                }
                
                //
                // evaluate
                $in_var = array( $in_field );
                $out_var = array();
                eval(isset($input['eval']['xmlrpc2orm']) ? $input['eval']['xmlrpc2orm'] : '$out_var[0] = $in_var[0];');
                $i = 0;
                foreach($input['orm']['field'] as $field) {
                    $orm = $obj->getVar($field['orm']);
                    if( !isset($out_var[$i]) ){
                        //this field musn't be transformed XML to ORM
                        continue;
                    }else if (strlen($out_var[$i])==0 ){
                        $orm->setDefault($field['field']);
                    }else{
                        $orm->setVar($field['field'], $out_var[$i], true);
                    }
                    $obj->setVar($field['orm'], $orm);
                    $i++;
                }
            }
        }
        return $obj;
    }
}

/**
 *
 * create XooNIpxXmlRpcTransform
 *
 *
 */
class XooNIpsXmlRpcTransformFactory
{
    function XooNIpsXmlRpcTransformFactory() 
    {
    }

    /**
     * return XooNIpsLogicFactory instance
     *
     * @return XooNIpsLogicFactory
     */
    function &getInstance() 
    {
        static $singleton = null;
        if (!isset($singleton)) $singleton = new XooNIpsXmlRpcTransformFactory();
        return $singleton;
    }

    /**
     * return XooNIpsLogic corresponding to $logic
     *
     * @param string $module module name
     * @param string $name name of Transform
     * @retval XooNIpsXmlRpcTransform corresponding to $module and $name
     * @retval false unknown logic
     */
    function &create($module, $name) 
    {
        static $falseVar = false;
        $logic = null;
        //
        $name = trim($name);
        if (false !== strstr($name, '..')) return $falseVar;
        $include_file = XOOPS_ROOT_PATH . "/modules/{$module}/class/xmlrpc/xmlrpctransform" . strtolower($name) . ".class.php";
        if (file_exists($include_file)) {
            include_once $include_file;
        } else {
            return $falseVar;
        }
        //
        if (strncmp('xnp', $module, 3) == 0) {
            $tok = substr($module, 3);
            $class = 'XNP' . ucfirst($tok) . 'XmlRpcTransform' . str_replace(' ', '', ucwords(str_replace('_', ' ', $name)));
        } else {
            $class = 'XooNIpsXmlRpcTransform' . str_replace(' ', '', ucwords(str_replace('_', ' ', $name)));
        }
        if (class_exists($class)) {
            $logic = new $class();
        }
        //
        if (!isset($logic)) {
            trigger_error('Handler does not exist. Name: ' . $name, E_USER_ERROR);
        }
        // return result
        if (isset($logic)) return $logic;
        else return $falseVar;
    }
}

/**
 *
 * create XooNIpxXmlRpcTransformCompo
 *
 *
 */
class XooNIpsXmlRpcTransformCompoFactory
{
    function XooNIpsXmlRpcTransformCompoFactory() 
    {
    }

    /**
     * return XooNIpsLogicFactory instance
     *
     * @return XooNIpsLogicFactory
     */
    function &getInstance() 
    {
        static $singleton = null;
        if (!isset($singleton)) $singleton = new XooNIpsXmlRpcTransformCompoFactory();
        return $singleton;
    }

    /**
     * return XooNIpsLogic corresponding to $logic
     *
     * @param string $module module name
     * @param string $name name of TransformCompo
     * @retval XooNIpsXmlRpcTransformCompo corresponding to $module and $name
     * @retval false unknown logic
     */
    function &create($module) 
    {
        static $falseVar = false;
        $compo = null;
        //
        $module = trim($module);
        $include_file = XOOPS_ROOT_PATH . "/modules/{$module}/class/xmlrpc/xmlrpctransformcompo.class.php";
        if (file_exists($include_file)) {
            include_once $include_file;
        }
        //
        if (strncmp('xnp', $module, 3) == 0) {
            $tok = substr($module, 3);
            $class = 'XNP' . ucfirst($tok) . 'XmlRpcTransformCompo';
        } else {
            return $falseVar;
        }
        if (class_exists($class)) {
            $compo = new $class();
        }else{
            //use XooNIpsXmlRpcTransformCompo if item type specific class is not eixsts
            $compo = new XooNIpsXmlRpcTransformCompo( $module );
        }
        //
        if (!isset($compo)) {
            trigger_error('Handler does not exist. Name: ' . $module, E_USER_ERROR);
        }
        // return result
        if (isset($compo)) return $compo;
        else return $falseVar;
    }
}
?>
