/*
 * Copyright 2006 Mask@ Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

maskat.loadEventFile = function(fileName){
  try {
    var eventDefDOM = maskat.loadXMLFile(fileName);

    maskat.loadLayoutEvents(eventDefDOM);
    maskat.loadComponentEvents(eventDefDOM);
  }
  catch (e){
    throw e;
  }
}

maskat.loadLayoutEvents = function(eventDefDOM){
  try {
    var eventNode = maskat.xpath(eventDefDOM, 'event[@id="onload"]');
    if (eventNode){
      var IDs = {component:null, event:"onload"};
      var onloadFunc = maskat.autoGenEventFunc(eventNode, eventDefDOM, IDs, null);
      onloadFunc();
    }
  }
  catch (e){
    throw e;
  }
}

maskat.loadComponentEvents = function(eventDefDOM){
  try{
    var componentNodes = maskat.xpath(eventDefDOM, 'component');
    for (var i = 0; i < componentNodes.length; i++){
      var componentNode = componentNodes[i];
      var componentID = maskat.xpath(componentNode, '@id');
      var eventNodes = maskat.xpath(componentNode, 'event');
      for (var j = 0; j < eventNodes.length; j++){
        var eventNode = eventNodes[j];
        var eventID = maskat.xpath(eventNode, '@id');
      
        var IDs = {component:componentID, event:eventID};
      
        var componentObj = null;
        try {
          componentObj = eval(IDs.component);
        }
        catch (e){
          throw new MaskatError({name: "EventXML Syntax Error",
                                 message: "コンポーネント'" + IDs.component + "'が画面定義XMLに定義されていません。",
                                 fileName: "intpr.js",
                                 functionName: "maskat.loadComponentEvents"});
        }
        if (componentObj[IDs.event]){
          componentObj[IDs.event] = maskat.autoGenEventFunc(eventNode, eventDefDOM, IDs, componentObj);
        }
        else{
          throw new MaskatError({name: "EventXML Syntax Error",
                                 message: "コンポーネント'" + IDs.component + "'にイベント'" + IDs.event + "'を設定できません。",
                                 fileName: "intpr.js",
                                 functionName: "maskat.loadComponentEvents"});
        }
      }
    }
  }
  catch (e){
    throw e;
  }
}


//イベント関数を自動生成する
maskat.autoGenEventFunc = function(eventNode, eventDefDOM, IDs, hostComponent){
  try {
  
    // <event ref="..." ...>の処理
    var eventRefID = maskat.xpath(eventNode, '@ref');
    if (eventRefID){
      var eventRefNode = maskat.xpath(eventDefDOM, 'eventRef[@id="' + eventRefID + '"]');
      if (eventRefNode){
        for (var i = 0; i < eventRefNode.childNodes.length; i++){
          eventNode.appendChild(eventRefNode.childNodes[i].cloneNode(true));
        }
        // eventNode.removeAttribute("ref");  // ref属性を二度と利用できなくする
      }
      else {
        throw new MaskatError({name: "EventXML Syntax Error",
                               message: "id値'" + eventRefID + "'を持つeventRef要素が見つかりません。",
                               fileName: "intpr.js",
                               functionName: "maskat.loadComponentEvents"});
      }
    }
  
    // <event type="local" ...>の処理
    if (maskat.xpath(eventNode, '@type') == "local"){
      return function(x){
        try {
          maskat.startFunc(eventNode, IDs);
          
          var validator = maskat.getValidator(false);
  
          // <result>の処理
          var resultNodes = maskat.xpath(eventNode, 'result');
          if (resultNodes.length == 1){
            var resultNode = resultNodes[0];
            var targetNodes = maskat.xpath(resultNode, 'target');
            for (var i = 0; i < targetNodes.length; i++){
              var targetNode = targetNodes[i];
              try {
                maskat.parseLocalTarget(targetNode, validator);
              }
              catch (e){
                if (e instanceof maskat.ValidationError){
                  validator.acceptError(e);
                }
                else {
                  throw e;
                }
              }
              if (validator.errors.length > 0){
                if (maskat.validationErrorHandler){
                  maskat.validationErrorHandler(validator.errors);
                }
                return;
              }
            }
          }
  
          maskat.finishFunc(eventNode);
        }
        catch (e){
          // これより上にエラーをthrowできないので(呼び出し元はWebブラウザとなる)、ここでalert文を出力する
          if (e instanceof MaskatError){
            alert("イベント実行時にエラーが発生しました。"
                 + "\nname: " + e.name + "\nmessage: " + e.message
                 + "\nfileName: " + e.fileName + "\nfunctionName: " + e.functionName);
          }
          else if (e instanceof MaskatProcessInterruption){
            // Do Nothing
          }
          else{
            throw e;
          }
        }
      }
    }
  
    // <event type="remote" ...>の処理
    else {
      return function(x){
        try {
          maskat.startFunc(eventNode, IDs);
          
          var validator = maskat.getValidator(false);

          // 送信電文の作成
          var sendXML;
          var paramNodes = maskat.xpath(eventNode, 'param');
          if (paramNodes.length == 1){
            var paramNode = paramNodes[0];
            var rootNode = maskat.xpath(paramNode, '@rootNode');
            var sourceNodes = maskat.xpath(paramNode, 'source');
          
            sendXML = '<' + rootNode;
            var ns = maskat.xpath(eventNode, 'param[1]/@ns');
            if (ns){
              sendXML += ' xmlns="' + ns + '"';
            }
            sendXML += '>';
            
            for (var i = 0; i < paramNode.childNodes.length; i++){
              var paramChildNode = paramNode.childNodes[i];
              if (paramChildNode.nodeType != 1){
                continue;
              }
              

              var parserFunc = maskat.intprSendTele;
              if (paramChildNode.prefix && paramChildNode.prefix != ""){
                var parserFuncName = "maskat." + paramChildNode.prefix + "SendTeleParser";
                parserFunc = eval(parserFuncName);
                if (!parserFunc){
                  throw new MaskatError({name: "Extended Syntax Error",
                                         message: "独自拡張タグ'" + paramChildNode.nodeName + "'を利用するには専用パーサとして'" + parserFuncName + "'関数を定義する必要があります。",
                                         fileName: "intpr.js",
                                         functionName: "maskat.autoGenEventFunc"});
                }
              }

              try{
                sendXML += parserFunc(paramChildNode, validator);
              }
              catch (e){
                if (e instanceof maskat.ValidationError){
                  validator.acceptError(e);
                }
                else {
                  throw e;
                }
              }
              if (validator.errors.length > 0){
                if (maskat.validationErrorHandler){
                  maskat.validationErrorHandler(validator.errors);
                }
                return;
              }
            }
            
            sendXML += '</' + rootNode + '>';

            var soap = maskat.xpath(paramNode, '@soap');
            if (soap=="true"){
                sendXML = maskat.soap.wrapMessage(sendXML,paramNode);
            }

            sendXML = '<?xml version="1.0" encoding="UTF-8"?>' + sendXML;
          }

          maskat.asyncTele(sendXML, eventNode, eventDefDOM, IDs, soap, hostComponent);
        }
        catch (e){
          // これより上にエラーをthrowできないので(呼び出し元はWebブラウザとなる)、ここでalert文を出力する
          if (e instanceof MaskatError){
            alert("イベント実行時にエラーが発生しました。"
                 + "\nname: " + e.name + "\nmessage: " + e.message
                 + "\nfileName: " + e.fileName + "\nfunctionName: " + e.functionName);
          }
          else if (e instanceof MaskatProcessInterruption){
            // Do Nothing
          }
          else{
            throw e;
          }
        }
      }
    }
  }
  catch (e){
    throw e;
  }
}

maskat.parseLocalTarget = function(targetNode, validator){
  try {
    var inName = maskat.xpath(targetNode, '@in');
    var inObj = maskat.getObjByName(inName);
    var inObjWrap;
    if (inObj){
      inObjWrap = new ObjWrapper(inObj);
    }

    var outName = maskat.xpath(targetNode, '@out');
    var outObj = maskat.getObjByName(outName);
    var outObjWrap;
    if (outObj){
      outObjWrap = new ObjWrapper(outObj);
    }
    
    var typeName = maskat.xpath(targetNode, '@type');

    if (!inName && outObjWrap){
      // out属性値のオブジェクトをクリア
      outObjWrap.clear();
    }
    else if (inObjWrap && outObjWrap && (maskat.isRialtoObj(outObj) || !outObjWrap.isSingleDataObj())){
      //FIXED outObjはjavascriptの場合は、outObjWrapのsetValueはエラーになるから、避ける
      var value = inObjWrap.getSingleValue();
      if (value && value != ""){
        if (typeName && typeName != ""){
          var descName = maskat.xpath(targetNode, '@desc');
          var minName = maskat.xpath(targetNode, '@min');
          var maxName = maskat.xpath(targetNode, '@max');
          var regexp = maskat.xpath(targetNode, '@regexp');

          validator.validate(inName, value, typeName, descName, minName, maxName, regexp);
        }
     
        // TODO: in,out両者が単一値オブジェクトであるかのチェック
        outObjWrap.setSingleValue(value);
      }
      else {
        if (typeName == "required"){
          var vTarget = new Object;
          vTarget.desc = descName;
          validator.acceptError(new maskat.ValidationError(vTarget, "入力してください。"));
        }
        else {
          return;
        }
      }
    }
    else if (inObjWrap && outName && 
       ((outObjWrap==null) || (!maskat.isRialtoObj(outObj) && outObjWrap.isSingleDataObj() ))){
      //FIXED outObjはjavascriptオブジェクトの場合この分岐に
      var value = inObjWrap.getSingleValue();
      if (value && value != ""){
        if (typeName && typeName != ""){
          var descName = maskat.xpath(targetNode, '@desc');
          var minName = maskat.xpath(targetNode, '@min');
          var maxName = maskat.xpath(targetNode, '@max');
          var regexp = maskat.xpath(targetNode, '@regexp');

          validator.validate(inName, value, typeName, descName, minName, maxName, regexp);
        }

        eval(outName + " = \"" + value + "\";");
        //FIXED valueを「"」で囲まれる、valueは文字列として扱われないと、混乱を招くかもしれない。例えば： true window ....
      }
      else {
        if (typeName == "required"){
          var vTarget = new Object;
          vTarget.desc = descName;
          validator.acceptError(new maskat.ValidationError(vTarget, "入力してください。"));
        }
        else {
          return;
        }
      }
    }    
    else {
      throw new MaskatError({name: "Mapping Error",
                             message: "in'" + inName + "'からout'" + outName + "'へのマッピングに対応していません。",
                             fileName: "intpr.js",
                             functionName: "maskat.parseLocalTarget"});
    }
  }
  catch (e){
    throw e;
  }
}

maskat.LEFTBRACKETREG = new RegExp("<","g");
maskat.RIGHTBRACKETREG = new RegExp(">","g");
maskat.AMPREG = new RegExp("\&","g");

maskat.toXMLData = function(value){
   value = ""+value;
   value = value.replace(maskat.AMPREG,"&amp;");
   value = value.replace(maskat.LEFTBRACKETREG,"&lt;");
   value = value.replace(maskat.RIGHTBRACKETREG,"&gt;");
   return value;;
}

maskat.intprSendTele = function(sourceNode, validator){
  try {
    var objName = maskat.xpath(sourceNode, '@obj');
    var objObj = maskat.getObjByName(objName);

    if (objObj == null){
      throw new MaskatError({name: "Mapping Error",
                             message: "送信元オブジェクト'" + objName + "'が未定義です。",
                             fileName: "intpr.js",
                             functionName: "maskat.intprSendTele"});
    }
    
    var teleType = maskat.xpath(sourceNode,"@teleType");
    if (teleType!=null){
        var teleMaker = ObjWrapper.findTeleMaker(teleType);
        if (teleMaker==null){
            throw new MaskatError({name: "teleTypeエラー",
                             message: "sourceの属性teleType:["+teleType+"]は定義されていませんでした。",
                             fileName: "intpr.js",
                             functionName: "maskat.intprSendTele"});
            
        }
        return teleMaker.make(objObj,validator,sourceNode);
    }
    
    var objObjWrap = new ObjWrapper(objObj);

    if (objObjWrap.isSingleDataObj()){
      //// 単一値を送信
      
      var value = objObjWrap.getSingleValue();

      var typeName = maskat.xpath(sourceNode, '@type');
      var descName = maskat.xpath(sourceNode, '@desc');

      var sendBlankElementName = maskat.xpath(sourceNode, '@sendBlankElement');

      // 非空白の入力フィールドしかXML要素を生成しない
      if ((value!=null && value != "") || sendBlankElementName == "true"){
        var nodeName = maskat.xpath(sourceNode, '@node');
        
        var minName = maskat.xpath(sourceNode, '@min');
        var maxName = maskat.xpath(sourceNode, '@max');
        
        var regexp = maskat.xpath(sourceNode, '@regexp');
        
        validator.validate(objName, value, typeName, descName, minName, maxName, regexp);
        return "<" + nodeName + ">" + maskat.toXMLData(value) + "</" + nodeName + ">";
      }
      else {
        if (typeName == "required"){
          var vTarget = new Object;
          vTarget.desc = descName;
          validator.acceptError(new maskat.ValidationError(vTarget, "入力してください。"));
        }
        else {
          return "";
        }
      }
    }
    else if (objObjWrap.isMultiDataObj()){
      var fromkeyName = maskat.xpath(sourceNode, '@fromkey');
      var idxRefName = maskat.xpath(sourceNode, '@idxRef');

      if (fromkeyName){
        //// 選択行(最近選択した1行)の特定列の値(単一値)を送信

        var selectedIndex = objObjWrap.getSelectedIndex();
        if (selectedIndex != -1){
        
          var targetObjWrap = objObjWrap;
        
          var idxRefName = maskat.xpath(sourceNode, '@idxRef');  // TODO: 二重宣言
          var idxRefObj = maskat.getObjByName(idxRefName);
          if (idxRefObj){
            var idxRefObjWrap = new ObjWrapper(idxRefObj);
            if (!idxRefObjWrap.isMultiDataObj()){
              throw new MaskatError({name: "Mapping Error",
                                     message: "idxRefは複数データ型オブジェクトを指定して下さい",  // TODO:詳細に
                                     fileName: "intpr.js",
                                     functionName: "maskat.intprSendTele"});
            }
            targetObjWrap = idxRefObjWrap;
          }

          var value = targetObjWrap.getValueFromMultiData(selectedIndex, fromkeyName);
         
          var sendBlankElementName = maskat.xpath(sourceNode, '@sendBlankElement');
      
          if ((value && value != "") || sendBlankElementName == "true"){
            var nodeName = maskat.xpath(sourceNode, '@node');
            return "<" + nodeName + ">" + maskat.toXMLData(value) + "</" + nodeName + ">";
          }
        }
      }
      else if (idxRefName){
        //// 選択行データの送信

        if (objObjWrap.isMultiSelect()){
          // 複数行選択
          var childNodeName = maskat.xpath(sourceNode, '@childNode');
          if (!childNodeName){
            throw new MaskatError({name: "EventXML Syntax Error",
                                   message: "複数行選択データの送信マッピングの際には、childNode属性の指定が必須です。",  // TODO:詳細に
                                   fileName: "intpr.js",
                                   functionName: "maskat.intprSendTele"});
          }
          var nodeName = maskat.xpath(sourceNode, '@node');
          
          var indexArray = objObjWrap.getSelectedIndexes();
          
          var bindNodes = maskat.xpath(sourceNode, 'bind');
          if (bindNodes.length == 0){
            return "";
          }
          var bindMapping = new Object();
          for (i = 0; i < bindNodes.length; i++){
            var bindNode = bindNodes[i];
            var fromkeyValue = maskat.xpath(bindNode, '@fromkey');
            var nodeValue = maskat.xpath(bindNode, '@node');
            bindMapping[fromkeyValue] = new Array;
            bindMapping[fromkeyValue][0] = nodeValue;
            if (maskat.xpath(bindNode, '@sendBlankElement')=="true"){
                bindMapping[fromkeyValue][1] = true;
            }else{
                bindMapping[fromkeyValue][1] = false;
            }
          }

          var targetObjWrap = objObjWrap;

          var idxRefName = maskat.xpath(sourceNode, '@idxRef');
          var idxRefObj = maskat.getObjByName(idxRefName);
          if (idxRefObj){
            var idxRefObjWrap = new ObjWrapper(idxRefObj);
            if (!idxRefObjWrap.isMultiDataObj()){
              throw new MaskatError({name: "Mapping Error",
                                     message: "idxRefは複数データ型オブジェクトを指定して下さい",  // TODO:詳細に
                                     fileName: "intpr.js",
                                     functionName: "maskat.intprSendTele"});
            }
            targetObjWrap = idxRefObjWrap;
          }
          
          var resultXML = "";

          //var sendBlankElementName = maskat.xpath(sourceNode, '@sendBlankElement');

          for (var i = 0; i < indexArray.length; i++){
            var resultXMLRow = "";
            for (var fromkeyValue in bindMapping){
              var value = targetObjWrap.getValueFromMultiData(indexArray[i], fromkeyValue);
              var sendBlankElement = bindMapping[fromkeyValue][1];
              if ((value && value != "") || sendBlankElement == true){
                resultXMLRow += "<" + bindMapping[fromkeyValue][0] + ">" + maskat.toXMLData(value) + "</" + bindMapping[fromkeyValue][0] + ">";
              }
            }
            if (resultXMLRow != ""){
              resultXMLRow = "<" + childNodeName + ">" + resultXMLRow + "</" + childNodeName + ">";
            }
            resultXML += resultXMLRow;
          }
          if (resultXML != "" && nodeName!=null && nodeName!=""){
            resultXML = "<" + nodeName + ">" + resultXML + "</" + nodeName + ">";
          }
          
          return resultXML;
        }
        else {
          // 単一行選択

          var index = objObjWrap.getSelectedIndex();
          if (index == -1){
            return "";
          }
          
          var nodeName = maskat.xpath(sourceNode, '@node');
          
          var bindNodes = maskat.xpath(sourceNode, 'bind');
          if (bindNodes.length == 0){
            return "";
          }
          var bindMapping = new Object();
          for (i = 0; i < bindNodes.length; i++){
            var bindNode = bindNodes[i];
            var fromkeyValue = maskat.xpath(bindNode, '@fromkey');
            var nodeValue = maskat.xpath(bindNode, '@node');
            bindMapping[fromkeyValue] = new Array;
            bindMapping[fromkeyValue][0] = nodeValue;
            if (maskat.xpath(bindNode, '@sendBlankElement')=="true"){
                bindMapping[fromkeyValue][1] = true;
            }else{
                bindMapping[fromkeyValue][1] = false;
            }
          }
          
          var targetObjWrap = objObjWrap;

          var idxRefName = maskat.xpath(sourceNode, '@idxRef');
          var idxRefObj = maskat.getObjByName(idxRefName);
          if (idxRefObj){
            var idxRefObjWrap = new ObjWrapper(idxRefObj);
            if (!idxRefObjWrap.isMultiDataObj()){
              throw new MaskatError({name: "Mapping Error",
                                     message: "idxRefは複数データ型オブジェクトを指定して下さい",  // TODO:詳細に
                                     fileName: "intpr.js",
                                     functionName: "maskat.intprSendTele"});
            }
            targetObjWrap = idxRefObjWrap;
          }
          
          var resultXML = "";
          
          //var sendBlankElementName = maskat.xpath(sourceNode, '@sendBlankElement');

          for (var fromkeyValue in bindMapping){
            var value = targetObjWrap.getValueFromMultiData(index, fromkeyValue);
            var sendBlankElement = bindMapping[fromkeyValue][1];
            if ((value && value != "") || sendBlankElement == true){
              resultXML += "<" + bindMapping[fromkeyValue][0] + ">" + maskat.toXMLData(value) + "</" + bindMapping[fromkeyValue][0] + ">";
            }
          }

          if (nodeName!=null && nodeName!="") 
             resultXML = "<" + nodeName + ">" + resultXML + "</" + nodeName + ">";
  
          return resultXML;
        }
      }
      else {
        // 複数データ型のデータ全てを送信

        var childNodeName = maskat.xpath(sourceNode, '@childNode');
        if (childNodeName){
          // TODO: MaskatError
        }
        var nodeName = maskat.xpath(sourceNode, '@node');
      
        var bindNodes = maskat.xpath(sourceNode, 'bind');
        if (bindNodes.length == 0){
          return "";
        }
        var bindMapping = new Object();
        for (var i = 0; i < bindNodes.length; i++){
          var bindNode = bindNodes[i];
          var fromkeyValue = maskat.xpath(bindNode, '@fromkey');
          var nodeValue = maskat.xpath(bindNode, '@node');
          bindMapping[fromkeyValue] = new Array;
          bindMapping[fromkeyValue][0] = nodeValue;
            if (maskat.xpath(bindNode, '@sendBlankElement')=="true"){
                bindMapping[fromkeyValue][1] = true;
            }else{
                bindMapping[fromkeyValue][1] = false;
            }
        }

        var resultXML = "";
        
        //var sendBlankElementName = maskat.xpath(sourceNode, '@sendBlankElement');

        for (i = 0; i < objObjWrap.getRowNum(); i++){
          var resultXMLRow = "";
          for (var fromkeyValue in bindMapping){
            var value = objObjWrap.getValueFromMultiData(i, fromkeyValue);
            var sendBlankElement = bindMapping[fromkeyValue][1];
            if ((value && value != "") || sendBlankElement == true){
              resultXMLRow += "<" + bindMapping[fromkeyValue][0] + ">" + maskat.toXMLData(value) + "</" + bindMapping[fromkeyValue][0] + ">";
            }
          }
          if (resultXMLRow != ""){
            resultXMLRow = "<" + childNodeName + ">" + resultXMLRow + "</" + childNodeName + ">";
          }
          resultXML += resultXMLRow;
        }
        if (resultXML != "" && nodeName!=null && nodeName!=""){
          resultXML = "<" + nodeName + ">" + resultXML + "</" + nodeName + ">";
        }
        return resultXML;
      }
    }
  }
  catch (e){
    throw e;
  }
}

maskat.toParamStr = function(obj){
    if (obj==null || obj==undefined){
        return "";
    }
    return obj.toString();
}


maskat.intprRecvTele = function(eventNode, teleDOM){
  try {
    var eventType = maskat.xpath(eventNode, '@type');
    var resultNodes = maskat.xpath(eventNode, 'result');
    var resultNode;
    if (resultNodes.length == 1){
      resultNode = resultNodes[0];
    }
    
    if (teleDOM.nodeName == "errors"){
      var onErrorTele;
      if (resultNode){
        onErrorTele = maskat.xpath(resultNode, '@onErrorTele');
      }
      
      if (onErrorTele){
        try {
          eval(onErrorTele + "(teleDOM);");
        }
        catch (e){
          throw new MaskatError({name: "onErrorTele Error",
                                 message: "onErrorTele関数が定義されていません。",
                                 fileName: "intpr.js",
                                 functionName: "maskat.intprRecvTele"});
        }
      }
      else {
        // デフォルトのエラー電文処理関数で処理
        maskat.processErrorTele(teleDOM);
      }
      throw new MaskatProcessInterruption(
          {name: "intprRecvTele Function Interruption",
           message: "エラー電文処理関数実行後の処理停止",
           fileName: "intpr.js",
           functionName: "maskat.intprRecvTele"});
    }
    else if (resultNode) {
      var rootNode = maskat.xpath(resultNode, '@rootNode');
      var soap = maskat.xpath(resultNode, '@soap');
      if (soap=="true"){
          teleDOM = maskat.soap.unwrapDOM(teleDOM,resultNode);
      }
      
      if (teleDOM.nodeName != rootNode){
        throw new MaskatError({name: "Received Tele Error",
                               message: "受信電文XMLのルートノード名'" + teleDOM.nodeName + "'とイベント定義XMLで指定した受信電文XMLのルートノード名'" + rootNode + "'が一致していません。",
                               fileName: "intpr.js",
                               functionName: "maskat.intprRecvTele"});
      }
      else{
        var eventNodeType = maskat.xpath(eventNode, '@type');
        var targetNodes = maskat.xpath(resultNode, 'target');
        for (var i = 0; i < targetNodes.length; i++){
          var targetNode = targetNodes[i];
          var targetNodeType = maskat.xpath(targetNode, '@type');
          if (targetNodeType != "local" && eventNodeType != "local"){
            maskat.parseRemoteTarget(targetNode, teleDOM);
          }
          else {
            maskat.parseLocalTarget(targetNode);
          }
        }
      }
    }

    //maskat.finishFunc(eventNode);
  }
  catch (e){
    throw e;
  }
}

maskat.parseRemoteTarget = function(targetNode, teleDOM){
  try {
    var outName = maskat.xpath(targetNode, '@out');
    var outObj = maskat.getObjByName(outName);
    var outObjWrap;
    if (outObj){
      outObjWrap = new ObjWrapper(outObj);
    }

    //targetNodeのteleType属性によって、カスタマイズwrapperバインディング
    var teleType = maskat.xpath(targetNode, '@teleType');
    if (teleType!=null){
        var pluggableTeleConsumer = ObjWrapper.findTeleConsumer(teleType);
        if (pluggableTeleConsumer==null){
            throw new MaskatError({name: "teleTypeエラー",
                             message: "teleType:["+teleType+"]は定義されていませんでした。",
                             fileName: "intpr.js",
                             functionName: "maskat.parseRemoteTarget"});
        }
        pluggableTeleConsumer.consume(outObj,maskat.xpath(teleDOM, maskat.xpath(targetNode, '@in')));
        return;
    }

    // 受信電文のデータ形式と、出力先オブジェクトの受け入れ可能データ形式との整合性チェック
    var isInSingleData = false;
    var isInMultiData = false;
    var isOutJSObj = false;
    var isOutSingleData = false;
    var isOutMultiData = false;
    if (maskat.xpath(targetNode, '@inkey')){
      isInMultiData = true;
    }
    else {
      isInSingleData = true;
    }
    if (!outObjWrap || outObjWrap.isJSObj()){
      isOutJSObj = true;
    }
    if (outObjWrap && outObjWrap.isSingleDataObj()){
      isOutSingleData = true;
    }
    else if (outObjWrap && outObjWrap.isMultiDataObj()){
      isOutMultiData = true;
    }
    if (!(isOutJSObj || (isInSingleData && isOutSingleData) || (isInMultiData && isOutMultiData))){
      var inDataType = "未対応型";
      var outDataType = "未対応型";
      if (isInSingleData){
        inDataType = "単一データ型";
      }
      else if (isInMultiData){
        inDataType = "複数データ型";
      }
      if (isOutSingleData && isOutMultiData){
        outDataType = "単一/複数両用データ型";
      }
      else if (isOutSingleData){
        outDataType = "単一データ型";
      }
      else if (isOutMultiData){
        outDataType = "複数データ型";
      }
      throw new MaskatError({name: "Mapping Error",
                             message: "受信電文のデータ形式(" + inDataType + ")と、出力先オブジェクトの受け入れ可能データ形式(" + outDataType + ")が一致していません。",
                             fileName: "intpr.js",
                             functionName: "maskat.parseRemoteTarget"});
    }

    // 受信電文データを取得
    var inData;
    if (isInSingleData){
      inData = maskat.xpath(teleDOM, maskat.xpath(targetNode, '@in') + '/text()');
    }
    else if (isInMultiData){
      var i;
      var bindNodes = maskat.xpath(targetNode, 'bind');
      var bindMapping;
      if (bindNodes.length != 0){
        bindMapping = new Object();
        for (i = 0; i < bindNodes.length; i++){
          var bindNode = bindNodes[i];
          var tokeyValue = maskat.xpath(bindNode, '@tokey');
          var nodeValue = maskat.xpath(bindNode, '@node');
          bindMapping[nodeValue] = tokeyValue;
        }
      }
 
      var inValue = maskat.xpath(targetNode, '@in');
      var inkeyValue = maskat.xpath(targetNode, '@inkey');
      var xpath;
      if (inValue==null || inValue==""){
          xpath = inkeyValue;
      }else{
          xpath = inValue + "[1]/" + inkeyValue;
      }
      var teleRowNodes = maskat.xpath(teleDOM, xpath);
      if (teleRowNodes.length == 0){
        // 受信電文にgridデータが無い
        return;
      }

      var inData = new Array(teleRowNodes.length);
      for (i = 0; i < inData.length; i++){
        inData[i] = new Object();
      }

      if (bindMapping){
        for (i = 0; i < teleRowNodes.length; i++){
          var teleRowNode = teleRowNodes[i];
          for (var nodeValue in bindMapping){
            var colNodes = maskat.xpath(teleRowNode, nodeValue);
            if (colNodes.length == 1){
              inData[i][bindMapping[nodeValue]] = maskat.xpath(colNodes[0], "text()");
            }
          }
        }
      }
      else {
        for (i = 0; i < teleRowNodes.length; i++){
          var teleRowNode = teleRowNodes[i];
          for (var j = 0; j < teleRowNode.childNodes.length; j++){
            if (teleRowNode.childNodes[j].nodeType != 1){
              continue;
            }
            if (teleRowNode.childNodes[j].hasChildNodes())
                inData[i][teleRowNode.childNodes[j].nodeName] = teleRowNode.childNodes[j].childNodes[0].nodeValue;
            else
                inData[i][teleRowNode.childNodes[j].nodeName] = "";
          }
        }
      }
    }

    /*
    // 受信電文データのデバッグ出力
    if (maskat.isArray(inData)){
      for (var i = 0; i < inData.length; i++){
        for (var j in inData[i]){
          alert(inData[i][j]);
        }
      }
    }
    else{
      alert(inData);
    }
    */

    // 出力先オブジェクトに受信電文データを設定
    if (isOutJSObj){
      eval(outName + " = inData");
    }
    else if (outObjWrap && outObjWrap.isSingleDataObj()){
      outObjWrap.setSingleValue(inData);
    }
    else if (outObjWrap && outObjWrap.isMultiDataObj()){
      outObjWrap.setMultiValue(inData);
    }
  }
  catch (e){
    throw e;
  }
}

//グローバル範囲でname変数を取得する、定義されなければ、undefinedを戻り値とする
maskat.getObjByName = function(name){
  var obj = null;
  try{
    obj = eval(name);
  }
  catch (e){
  }
  return obj;
}


// デフォルトのエラー電文処理関数
maskat.processErrorTele = function(teleDOM){
  try {
    var errorCodeText = maskat.xpath(teleDOM, 'error/errorCode/text()');
    var messageCodeText = maskat.xpath(teleDOM, 'error/messageCode/text()');
    var messageText = maskat.xpath(teleDOM, 'error/message/text()');
    var infoText = maskat.xpath(teleDOM, 'error/info/text()');
    var systemErrorMessageText = maskat.xpath(teleDOM, 'error/systemErrorMessage/text()');

    var errorMsg = "エラーが発生しました。\nerrorCode:" + errorCodeText + "\nmessageCode:"
                   + messageCodeText + "\nmessage:" + messageText;
    if (infoText){
      errorMsg += "\ninfo:" + infoText;
    }
    if (systemErrorMessageText){
      errorMsg += "\nsystemErrorMessage:" + systemErrorMessageText;
    }

    throw new MaskatError({name: "Received Tele Error",
                           message: errorMsg,
                           fileName: "intpr.js",
                           functionName: "maskat.processErrorTele"});
  }
  catch (e){
    throw e;
  }
}





// 同期通信でサーバからXMLファイルを取得し、そのDOMのdocumentElementを返す
maskat.loadXMLFile = function(xmlFile){
  var xhr;
  try {
    xhr = new ActiveXObject("Msxml2.XMLHTTP");
  } catch (e) {
    try {
      xhr = new ActiveXObject("Microsoft.XMLHTTP");
    } catch (e) {
      xhr = false;
    }
  }
  if (!xhr && typeof XMLHttpRequest != 'undefined') {
    xhr = new XMLHttpRequest();
  }
  if (xhr) {
    xhr.open("GET", xmlFile, false);
    xhr.send(null);
    if (xhr.readyState == 4){
      if (xhr.status == 200) {
        if (xhr.responseXML && xhr.responseXML.nodeType == 9){
          return(xhr.responseXML.documentElement);
        }
        else {
          throw new MaskatError({name: "File Error",
                               message: "サーバから取得したファイル'" + xmlFile + "'が正規のXML文書ではありません。\nファイル内のテキスト文字列: " + xhr.responseText,
                               fileName: "intpr.js",
                               functionName: "maskat.loadXMLFile"});
        }
      }
      else {
        throw new MaskatError({name: "XMLHttpRequest Error",
                               message: "サーバからファイル'" + xmlFile + "'を正常に取得できませんでした。\nXMLHttpRequest#readyState: " + xhr.readyState + "\nXMLHttpRequest#status: " + xhr.status,
                               fileName: "intpr.js",
                               functionName: "maskat.loadXMLFile"});
      }
    }
  }
  else{
    throw new MaskatError({name: "XMLHttpRequest Error",
                           message: "XMLHttpRequestオブジェクトが取得できませんでした。",
                           fileName: "intpr.js",
                           functionName: "maskat.loadXMLFile"});
  }
}

maskat.asyncTele = function(data, eventNode, eventDefDOM, IDs, soap, hostComponent){
  try {
    // XMLHttpRequestオブジェクトの取得
    var xhr;
    try {
      xhr = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
      try {
        xhr = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (e) {
        xhr = false;
      }
    }
    if (!xhr && typeof XMLHttpRequest != 'undefined') {
      xhr = new XMLHttpRequest();
    }
    if (!xhr) {
      throw new MaskatError({name: "XMLHttpRequest Error",
                             message: "XMLHttpRequestオブジェクトが取得できませんでした。",
                             fileName: "intpr.js",
                             functionName: "maskat.asyncTele"});
    }

    // remoteUrl属性値の取得
    var remoteUrl = maskat.xpath(eventNode, '@remoteUrl');
    if (!remoteUrl){
      remoteUrl = maskat.xpath(eventDefDOM, 'remoteUrl/@url');
      if (!remoteUrl){
        throw new MaskatError({name: "EventXML Syntax Error",
                               message: "リモート通信するイベント要素に対してはremoteUrlの指定が必須です。",
                               fileName: "intpr.js",
                               functionName: "maskat.asyncTele"});
      }
    }

    // async属性値の取得
    var async = maskat.xpath(eventNode, '@async');
    if (async==null || async == "true"){//デフォルトは非同期
      async = true;
    }
    else {
      async = false;
    }
    xhr.open("POST", remoteUrl, true);  // async=true(同期通信)でも内部的には非同期通信を行う

    // ヘッダ処理
    var headerNodes = maskat.xpath(eventNode, 'header');
    for (var i = 0; i < headerNodes.length; i++){
      xhr.setRequestHeader(maskat.xpath(headerNodes[i], '@name'), maskat.xpath(headerNodes[i], '@value'));
    }
    headerNodes = maskat.xpath(eventDefDOM, 'header');
    for (var i = 0; i < headerNodes.length; i++){
      xhr.setRequestHeader(maskat.xpath(headerNodes[i], '@name'), maskat.xpath(headerNodes[i], '@value'));
    }
    if (soap=="true"){
       xhr.setRequestHeader("Content-Type", "application/soap+xml");
    }
    else {
       xhr.setRequestHeader("Content-Type", "application/xml");
    }
    if (IDs.component){
      xhr.setRequestHeader("maskat_componentID", IDs.component);
    }
    xhr.setRequestHeader("maskat_eventID", IDs.event);

    // タイムアウト
    var timeout = maskat.xpath(eventNode, '@timeout');
    if (timeout){
      var onTimeoutError = maskat.xpath(eventNode, '@onTimeoutError');
      if (onTimeoutError){
        try {
          onTimeoutError = eval(onTimeoutError);
        }
        catch (e){
          throw new MaskatError({name: "onTimeoutError Error",
                                 message: "onTimeoutError関数' + onTimeoutError + 'が定義されていません。",
                                 fileName: "intpr.js",
                                 functionName: "maskat.asyncTele"});
        }
      }
      else {
        // デフォルトのonTimeoutError関数で処理
        onTimeoutError = maskat.defaultOnTimeoutError;
      }
    }
    
    
    var sendObj = new maskat.sendObj({xhr: xhr, data: data, async: async,
                        eventNode: eventNode, IDs: IDs, timeout: timeout,
                        onTimeoutError: onTimeoutError, hostComponent: hostComponent});
    
    // コールバック関数の設定
    var callbackFunc = maskat.autoCallBackFunc(sendObj);
    xhr.onreadystatechange = callbackFunc;
    
    maskat.globalVar.sendQueueThread.add(sendObj);

    if (CrossBrowser.isFF && async == "false"){
      // FireFoxのXMLHttpRequest同期通信ではコールバックとしてonreadystatechangeは呼ばれないので、ここで明示的に呼ぶ
      callbackFunc();
    }

  }
  catch (e){
    if (sendObj!=null && sendObj.async!=true){
      sendObj.commuEnd();
    }
    throw e;
  }
}


maskat.globalVar.sendObjUniqueID = 0;
maskat.globalVar.syncConn = false;  // 起動時はfalse


maskat.sendObj = function(param){
  this.id = maskat.globalVar.sendObjUniqueID++;
  this.xhr = param.xhr;
  this.data = param.data;
  this.async = param.async;
  this.eventNode = param.eventNode;
  this.IDs = param.IDs;
  this.timeout = param.timeout;
  this.onTimeoutError = param.onTimeoutError;
  this.hostComponent = param.hostComponent;
}
maskat.sendObj.prototype.send = function(){
  maskat.beforeFunc(this.eventNode, this.xhr, this.data, this.IDs);
  
  maskat.globalVar.processingHash.add(this);
  if (this.hostComponent instanceof rialto.widget.Button && this.hostComponent.setEnable != null
          && this.async == false){
      this.hostComponent.setEnable(false);
  }
  
  if (this.timeout){
    this.onTimeoutErrorID = setTimeout("maskat.globalVar.processingHash.timeout(" + this.id + ")" , this.timeout);
  }
  
  this.xhr.send(this.data);
}
maskat.sendObj.prototype.receive = function(){
  maskat.autoCallBackFunc2(this);
}
maskat.sendObj.prototype.commuEnd = function(){
  if (this.hostComponent instanceof rialto.widget.Button && this.hostComponent.setEnable != null
          && this.async == false){
      this.hostComponent.setEnable(true);
      this.hostComponent = null;
  }
  if (this.async!=true && maskat.globalVar.syncConnDialog!=null && 
      maskat.globalVar.syncConnDialog.remove!=null){
       // ダイアログボックス閉じる
       maskat.globalVar.syncConnDialog.remove();
       maskat.globalVar.syncConnDialog = null;
       // 同期通信
       maskat.globalVar.syncConn = false;
  }
}


maskat.sendQueueThread = function(){
  this.queue = new Array();
}
maskat.sendQueueThread.prototype.add = function(obj){
  this.queue.push(obj);
  this.sendCheck();
}
maskat.sendQueueThread.prototype.sendCheck = function(){
 try{
  var thisObj = maskat.globalVar.sendQueueThread;
  if (thisObj.queue.length > 0){
    if (!maskat.globalVar.syncConn){
      maskat.globalVar.syncConn = true;  // 同期/非同期に関わらずすぐにロックをかける
      var sendObj = thisObj.queue.shift();
      if (!sendObj.async){
        // ダイアログボックス表示
        var _maskat_connecting_popup = new rialto.widget.PopUp('_maskat_connecting_popup', 50, 50, 250, 50,
            null, '通信中', 'Transparent', null, true, null);
        var _maskat_connecting_label = new rialto.widget.Label('_maskat_connecting_label', 10, 10,
            _maskat_connecting_popup, '通信中です。しばらくお待ち下さい。', null,
            {name:'_maskat_connecting_label', top:10, left:10, text:'通信中です。しばらくお待ち下さい。',
            parent:_maskat_connecting_popup});
        maskat.globalVar.syncConnDialog = _maskat_connecting_popup;
      }
      else {
        maskat.globalVar.syncConn = false;  // 非同期であればこの時点でロック解除
      }
      sendObj.send();
      thisObj.sendCheck();
    }
    else {
      setTimeout(thisObj.sendCheck, 500);
    }
   }
 }catch(e){
      if (sendObj!=null)
         sendObj.commuEnd();

      // これより上にエラーをthrowできないので(非同期通信時のmaskat.autoCallBackFuncの
      // 呼び出し元はWebブラウザとなる)、ここでalert文を出力
      if (e instanceof MaskatError){
        alert("maskat.autoCallBackFunc呼び出し時にエラーが発生しました。"
             + "\nname: " + e.name + "\nmessage: " + e.message
             + "\nfileName: " + e.fileName + "\nfunctionName: " + e.functionName);
      }
  }
}
maskat.globalVar.sendQueueThread = new maskat.sendQueueThread();


maskat.processingHash = function(){
  this.hash = new Object();
}
maskat.processingHash.prototype.add = function(obj){
  this.hash[obj.id] = obj;
}
maskat.processingHash.prototype.remove = function(obj){
  this.hash[obj.id] = undefined;
}
maskat.processingHash.prototype.timeout = function(id){
  var timeoutObj = this.hash[id];
  
  timeoutObj.xhr.abort();
  timeoutObj.commuEnd();
  timeoutObj.onTimeoutError();
  this.hash[id] = undefined;
}
maskat.globalVar.processingHash = new maskat.processingHash();


maskat.receiveQueueThread = function(){
  this.queue = new Array();
}
maskat.receiveQueueThread.prototype.add = function(obj){
  this.queue.push(obj);
  this.receiveCheck();
}
maskat.receiveQueueThread.prototype.receiveCheck = function(){
  var thisObj = maskat.globalVar.receiveQueueThread;
  if (thisObj.queue.length > 0){
    if (!maskat.globalVar.syncConn){
      var receiveObj = thisObj.queue.shift();
      receiveObj.receive();
      thisObj.receiveCheck();
    }
    else {
      setTimeout(thisObj.receiveCheck, 500);
    }
  }
}
maskat.globalVar.receiveQueueThread = new maskat.receiveQueueThread();


maskat.defaultOnTimeoutError = function(){
  alert("defaultOnTimeoutError");
}


maskat.autoCallBackFunc = function(sendObj){
  return function(){
    try{
      if (sendObj.xhr.readyState == 4){
        if (sendObj.xhr.status == 200){
        
          // タイムアウト解除
          if (sendObj.timeout){
            clearTimeout(sendObj.onTimeoutErrorID);
          }
          
          maskat.globalVar.processingHash.remove(sendObj.id);

          if (sendObj.async){
            // 非同期通信
            maskat.globalVar.receiveQueueThread.add(sendObj);
          }
          else {
            // 同期通信
            maskat.autoCallBackFunc2(sendObj);
          }
        }
        else if (sendObj.xhr.status == 0){
          // timeoutエラーのXHR#abort時
        }
        else{
          throw new MaskatError({name: "XMLHttpRequest Error",
                                 message: "サーバから受信電文を正常に取得できませんでした。\nXHR#readyState: " + sendObj.xhr.readyState + "\nXHR#status: " + sendObj.xhr.status,
                                 fileName: "intpr.js",
                                 functionName: "maskat.autoCallBackFunc"});
        }
      }
    }
    catch (e){
      if (sendObj!=null)
         sendObj.commuEnd();

      // これより上にエラーをthrowできないので(非同期通信時のmaskat.autoCallBackFuncの
      // 呼び出し元はWebブラウザとなる)、ここでalert文を出力
      if (e instanceof MaskatError){
        alert("maskat.autoCallBackFunc呼び出し時にエラーが発生しました。"
             + "\nname: " + e.name + "\nmessage: " + e.message
             + "\nfileName: " + e.fileName + "\nfunctionName: " + e.functionName);
      }
      else if (e instanceof MaskatProcessInterruption){
        // Do Nothing
      }
      else{
        throw e;
      }
    }
  };
}

maskat.autoCallBackFunc2 = function(sendObj){
  sendObj.recvDOM = sendObj.xhr.responseXML.documentElement;
  maskat.afterFunc(sendObj);
  maskat.intprRecvTele(sendObj.eventNode, sendObj.recvDOM);

  sendObj.commuEnd();
  maskat.finishFunc(sendObj.eventNode);
}


maskat.startFunc = function(eventNode, IDs){
  try {
    var startFunc = maskat.xpath(eventNode, '@start');
    if (startFunc){
      var param = new Object();
      param.stopProcess = false;
      try{
        eval(startFunc + "(param);");
      }
      catch (e){
        throw e;  // TODO: MaskatErrorに変更？IDs利用
      }
      if (param.stopProcess){
        throw new MaskatProcessInterruption(
          {name: "Start Function Interruption",
           message: "componentID'" + IDs.component + "',eventID'" + IDs.event + "'のstart関数内で処理停止",
           fileName: "intpr.js",
           functionName: "maskat.startFunc"});
      }
    }
  }
  catch (e){
    throw e;
  }
}

maskat.beforeFunc = function(eventNode, xhr, sendXML, IDs){
    var beforeFunc = maskat.xpath(eventNode, '@before');
    if (beforeFunc){
      var param = new Object();
      param.stopProcess = false;
      param.xhr = xhr;
      param.sendXML = sendXML;
      try{
        eval(beforeFunc + "(param);");
      }
      catch(e){
        throw e;  // TODO: MaskatErrorに変更？
      }
      if (param.stopProcess){
        throw new MaskatProcessInterruption(
          {name: "Before Function Interruption",
           message: "componentID'" + IDs.component + "',eventID'" + IDs.event + "'のbefore関数内で処理停止",
           fileName: "intpr.js",
           functionName: "maskat.beforeFunc"});
      }
    }
}

maskat.afterFunc = function(sendObj){
  try {
    var afterFunc = maskat.xpath(sendObj.eventNode, '@after');
    if (afterFunc){
      var param = new Object();
      param.stopProcess = false;
      param.xhr = sendObj.xhr;
      param.recvDOM = sendObj.recvDOM;
      try{
        eval(afterFunc + "(param);");
        sendObj.recvDOM = param.recvDOM;
      }
      catch(e){
        alert(e);  // TODO: エラーオブジェクト
      }
      if (param.stopProcess){
        throw new MaskatProcessInterruption(
          {name: "After Function Interruption",
           message: "componentID'" + sendObj.IDs.component + "',eventID'" + sendObj.IDs.event + "'のafter関数内で処理停止",
           fileName: "intpr.js",
           functionName: "maskat.afterFunc"});
      }
    }
  }
  catch (e){
    throw e;
  }
}

maskat.finishFunc = function(eventNode){
  try {
    var finishFunc = maskat.xpath(eventNode, '@finish');
    if (finishFunc){
      try{
        eval(finishFunc + "();");
      }
      catch(e){
        alert(e.message);  // TODO: エラーオブジェクトを返す
      }
    }
  }
  catch (e){
    throw e;
  }
}

/* Mask@版XPath制限事項
   ・ロケーションパスは相対ロケーションパスのみ指定可能。
   ・軸(axis)はchildとattributeのみ。childとattributeはそれぞれ省略記法のみ(child:(省略して何も書かない), attribute: @)
   ・ノードテスト(nodetest)は要素名(child軸の場合)、属性名(attribute軸の場合)、'text()'のみ
   ・述語(predicate)はchild軸に対してのみ利用可能
   ・述語は[n]か[@name="value"]のみ。左記述語を複数回並べて指定することを禁止。
   ・[注意] 述語の[n]のnは1-index。ゼロ始まりではない。
   Mask@版XPath拡張事項
   ・attribute軸はロケーションステップの最後、かつ単一Nodeに対してのみ利用可能。
     その場合、戻り値は属性名の文字列。属性が定義されていない場合はnull。
   ・text()ノードテストはロケーションステップの最後、かつ単一親Nodeに対してのみ利用可能。
     その場合、戻り値はテキスト文字列。
   ・ロケーションステップの最後に述語が使われていた場合、戻り値は単一Nodeとする(述語がマッチしなければnullを返す)。
   ・その他の場合の戻り値: Node集合
*/

maskat.xpath = function(dom, locationPath){
  var locationStep = locationPath.split("/");
  var i;
  var j;
  var k;
  
  if (!dom || dom.nodeType != 1){  // ELEMENT_NODE
    throw new MaskatError({name: "Mask@XPath Syntax Error",
                           message: "引数domが不正です。引数domにはDOMの要素ノードを指定して下さい。",
                           fileName: "intpr.js",
                           functionName: "maskat.xpath"});
  }
  
  var domArray = new Array();

  domArray.push(dom);
  
  var isLastPredicate = false;
  
  for (i = 0; i < locationStep.length; i++){
    var domNextArray = new Array();
    for (j = 0; j < domArray.length; j++){

      // 軸がattributeの場合
      if (locationStep[i].charAt(0) == "@"){
        if (i != locationStep.length - 1 || domArray.length != 1){
          throw new MaskatError({name: "Mask@XPath Syntax Error",
                                 message: "attribute軸はロケーションステップの最後、かつ単一Nodeに対してのみ利用可能です。\nエラーが発生したロケーションパス: " + locationPath,
                                 fileName: "intpr.js",
                                 functionName: "maskat.xpath"});
        }
        else {
          var attributeName = locationStep[i].substring(1, locationStep[i].length);
          var attributeNode = domArray[j].getAttributeNode(attributeName);
          if (attributeNode){
            // すぐに属性値(文字列)を返す
            return attributeNode.nodeValue;
          }
          else {
            // 属性が設定されていない場合はすぐにnullを返す
            return null;
          }
        }
      }
      
      // ノードテストがtext()のchild軸の場合
      else if (locationStep[i] == "text()"){
        if (i != locationStep.length - 1 || domArray.length != 1){
          throw new MaskatError({name: "Mask@XPath Syntax Error",
                                 message: "text()ノードテストはロケーションステップの最後、かつ単一親Nodeに対してのみ利用可能です。\nエラーが発生したロケーションパス: " + locationPath,
                                 fileName: "intpr.js",
                                 functionName: "maskat.xpath"});
        }
        else {
          if (domArray[j].childNodes && domArray[j].childNodes.length == 1
                                     && domArray[j].childNodes[0].nodeType == 3){  // TEXT_NODE
            return domArray[j].childNodes[0].nodeValue;
          }
          else if (domArray[j].childNodes && domArray[j].childNodes.length == 0){
            return "";
          }
          else {
            throw new MaskatError({name: "Mask@XPath Syntax Error",
                                   message: "text()ノードテストを指定したノードのXML形式が不正です。\nエラーが発生したロケーションパス: " + locationPath,
                                   fileName: "intpr.js",
                                   functionName: "maskat.xpath"});
          }
        }
      }
      
      // ノードテストが要素名のchild軸の場合
      else{

        var nodeTest = null;
        var predicate = null;
        var predicateAttributeName = null;
        var predicateAttributeValue = null;
        var nodePos = 0;
    
        if (locationStep[i].indexOf("[") != -1){
          nodeTest = locationStep[i].substring(0, locationStep[i].indexOf("["));
          predicate = locationStep[i].substring(locationStep[i].indexOf("[") + 1, locationStep[i].indexOf("]"));

          if (predicate.charAt(0) == "@"){
            predicateAttributeName = predicate.substring(1, predicate.indexOf("="));
            predicateAttributeValue = predicate.substring(predicate.indexOf("=") + 2, predicate.length - 1); // ダブルクォートのことを考慮に入れる
          }
          else {
            nodePos = parseInt(predicate);
            if (isNaN(nodePos)){
              throw new MaskatError({name: "Mask@XPath Syntax Error",
                                     message: "述語が不正です。\nエラーが発生したロケーションパス: " + locationPath,
                                     fileName: "intpr.js",
                                     functionName: "maskat.xpath"});
            }
          }
          
          if (i == locationStep.length - 1){
            isLastPredicate = true;
          }
          
        }
        else{
          nodeTest = locationStep[i];
        }
        
        var currentNodePos = 0;  // 述語[n]を処理するために利用

        for (k = 0; k < domArray[j].childNodes.length; k++){
          var childNode = domArray[j].childNodes[k];
          if (childNode.nodeType != 1){  // ELEMENT_NODE
            continue;
          }
          if (childNode.nodeName == nodeTest){
            currentNodePos++;
            if (predicate){
              if (predicateAttributeName){
                if (childNode.getAttribute(predicateAttributeName) != predicateAttributeValue){
                  continue;
                }
                else if (isLastPredicate){
                  return childNode;
                }
              }
              else {
                if (nodePos != currentNodePos){
                  continue;
                }
                else if (isLastPredicate){
                  return childNode;
                }
              }
            }
            domNextArray.push(childNode);
          }
        }
      }
    }
    domArray = domNextArray;
  }

  if (isLastPredicate){
    return null;
  }
  else {
    return domArray;
  }
}

