/*

Class SimpleURIResolver

  This class provides URI resolver function using
  RDF/XML-based simple mapping tables.

*/

/* Constructor */
  function SimpleURIResolver () {
  }

/* Namespaces */
  var NS_RDF  ="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
  var NS_PROP = "http://suika.fam.cx/admin/assign/urn-20050519#DT-";
  var NS_TYPES = "http://suika.fam.cx/~wakaba/2005/6/uri-table#";
  var NS_REPLACE = "http://suika.fam.cx/~wakaba/archive/2005/6/replace#";

/* Node Types */
  var ELEMENT_NODE = 1;
  var TEXT_NODE = 3;

/* Public Methods */

/*

  resolve
    Resolves a URI reference into another URI reference.

    Parameters
      origURI of type DOMString
        The original absolute URI reference.
      useSecondary of type boolean
        Whether secondary resolved URI references should 
        be returned if any or not.  If set to false,
        only primary resolved URI references are returned if any.

    Return Value
      DOMString  A resolved URI reference, or null if no
                 URI reference found.

    No Exception

*/
  SimpleURIResolver.prototype.resolve = function (origURI, useSecondary) {
    if (this.ResolveMatchTable.primary[origURI]) {
      return this.ResolveMatchTable.primary[origURI];
    } else if (useSecondary && this.ResolveMatchTable.secondary[origURI]) {
      return this.ResolveMatchTable.secondary[origURI];
    } else {
      return this.ResolveByTable (origURI, useSecondary, this.ResolveTable);
    }
  };

/*

  addMappingTableURI
    Appends an entry of a pair of a URI prefix and a URI reference
    from which a mapping table might be retrievable to the list
    of root mapping table.

    Parameters
      uriPrefix of type DOMString
        The prefix of URI references.  URI references passed 
        to the resolve method are tested against this prefix
        and if they does match the mapping table referenced by
        the tableURI is looked.
      tableURI of type DOMString
        An absolute URI reference.  It is expected that retrieving
        this URI reference results in an RDF/XML-based simple
        mapping table supported by this class.

    No Return Value
    No Exception

*/

  SimpleURIResolver.prototype.addMappingTableURI =
  function (uriPrefix, tableURI) {
    this.ResolveTable.push ({prefix: uriPrefix, see: tableURI});
  };

/* Internal Cache Tables */

  /* Mapping table constructed from tables added by addMappingTableURI
     method and tables referred by them. */
  SimpleURIResolver.prototype.ResolveTable = [];

  /* Mapping table looked up literally (URI reference as string). */
  SimpleURIResolver.prototype.ResolveMatchTable = {
    primary: {}, secondary: {}
  };

  /* List of URI references of tables, used not to open
     the same table twice and to avoid infinite loops caused
     by circlular references. */
  SimpleURIResolver.prototype.TableLoaded = {};

/* Internal Methods */

  /* Resolving URI reference by using ResolveTable. */
  SimpleURIResolver.prototype.ResolveByTable =
  function (origURI, useSecondary, table) {
    for (var i = 0; i < table.length; i++) {
      var item = table[i];
      if ((item.uri && item.uri == origURI) ||
          (item.prefix &&
           item.prefix == origURI.substring (0, item.prefix.length))) {
        if (item.table) {
          return this.ResolveByTable (origURI, useSecondary, item.table);
        } else if (item.primary || (useSecondary && item.secondary)) {
	  var repprop = {
            all: origURI,
            _: (item.uri ? '' : origURI.substring (item.prefix.length))
          };
	  if (item.primary) {
	    this.ResolveMatchTable.primary[origURI] =
	      this.ReplaceTemplate (item.primary, repprop);
            return this.ResolveMatchTable.primary[origURI];
          } else {
            this.ResolveMatchTable.primary[origURI] = null;
            this.ResolveMatchTable.secondary[origURI] =
	      this.ReplaceTemplate (item.secondary, repprop);
            return this.ResolveMatchTable.secondary[origURI];
          }
        } else if (item.see && !this.TableLoaded[item.see]) {
          item.table = [];
          this.TableLoaded[item.see] = true;
          var req = new XMLHttpRequest ();
          req.open ('GET', item.see, false);
          req.send (null);
          if (req.responseXML) {
            var db = req.responseXML;
            var dbitems = db.documentElement.childNodes;
            var dbitemsLength = dbitems.length;
            for (var j = 0; j < dbitemsLength; j++) {
              var dbitem = dbitems[j];
              if ((dbitem.nodeType == ELEMENT_NODE) &&
                  (dbitem.namespaceURI == NS_TYPES)) {
                var titem = {};
                if (dbitem.localName == 'Resource') {
                  if (dbitem.hasAttributeNS (NS_RDF, 'about')) {
                    titem.uri = dbitem.getAttributeNS (NS_RDF, 'about');
                  }
                } else if (dbitem.localName == 'ResourceGroup') {
                  if (dbitem.hasAttributeNS (NS_RDF, 'about')) {
                    titem.prefix = dbitem.getAttributeNS (NS_RDF, 'about');
                  }
                }
                var dbitemcs = dbitem.childNodes;
                var dbitemcsLength = dbitemcs.length;
                for (var k = 0; k < dbitemcsLength; k++) {
                  var dbitemc = dbitemcs[k];
                  if (dbitemc.namespaceURI == NS_PROP) {
                    if (dbitemc.localName == 'PRIMARY-RESOLVED-URI') {
                      titem.primary = dbitemc;
                    } else if (dbitemc.localName == 'SECONDARY-RESOLVED-URI') {
                      titem.secondary = dbitemc;
                    }
                  } else if (dbitemc.namespaceURI == NS_TYPES) {
                    if (dbitemc.localName == 'see') {
                      titem.see = dbitemc.getAttributeNS (NS_RDF, 'resource');
		                  /* TODO: Base URI */
                    }
                  }
                }
		item.table.push (titem);
              }
            }
            i--; /* Redo */
          } else {
            this.ResolveMatchTable.primary[origURI] = null;
            if (useSecondary) this.ResolveMatchTable.secondary[origURI] = null;
            return;
          }
        } else {
          this.ResolveMatchTable.primary[origURI] = null;
          if (useSecondary) this.ResolveMatchTable.secondary[origURI] = null;
          return;
        }
      }
    }
    this.ResolveMatchTable.primary[origURI] = null;
    if (useSecondary) this.ResolveMatchTable.secondary[origURI] = null;
  };

  /* Replacing template in mapping tables. */
  SimpleURIResolver.prototype.ReplaceTemplate =
  function (template, replaceTable) {
    var tempFrags = [template];
    var result = '';
    while (tempFrags.length > 0) {
      var tempFrag = tempFrags.pop ();
      if (tempFrag.nodeType == TEXT_NODE) {
        result += tempFrag.data;
      } else if (tempFrag.nodeType == ELEMENT_NODE) {
        if (tempFrag.namespaceURI == NS_REPLACE) {
          if (tempFrag.localName == 'Integer') {
            var rint = '';
            var digits = tempFrag.getAttributeNS (null, 'min-digits') || 0;
            rint = '' + parseInt (replaceTable[tempFrag.getAttributeNS
                                                 (null, 'source') || '_'], 10);
            while (rint.length < digits) {
              rint = '0' + rint;
            }
            result += rint;
          } else if (tempFrag.localName == 'String') {
            result += replaceTable[tempFrag.getAttributeNS
                                                 (null, 'source') || '_'];
                      /* Should already be escape-encoded */
          } else if (tempFrag.localName == 'If') {
            var tf = false;
            if (tempFrag.hasAttributeNS (null, 'test')) {
              if (!replaceTable['0']) {
                var sp = replaceTable['_'].split (/:/);
                for (var i = 0; i < sp.length; i++) {
                  replaceTable[i] = sp[i];
                }
              }
              var rept = replaceTable[tempFrag.getAttributeNS (null, 'test')];
              tf = (rept && rept.length > 0);
            } else {
              tf = (replaceTable['_'].length > 0);
            }

            var ifcs = tempFrag.childNodes;
            var ifcsLength = ifcs.length;
            for (var i = 0; i < ifcsLength; i++) {
              var ifc = ifcs[i];
              if (ifc.nodeType == ELEMENT_NODE &&
                  ifc.namespaceURI == NS_REPLACE) {
                if ((tf && ifc.localName == 'True') ||
                    (!tf && ifc.localName == 'False')) {
                  var ifccs = ifc.childNodes;
                  var ifccsLength = ifccs.length;
                  for (var j = ifccsLength - 1; j >= 0; j--) {
                    tempFrags.push (ifccs[j]);
                  }
                }
              }
            }
          }
        } else {
          var tempFragcs = tempFrag.childNodes;
          var tempFragcsLength = tempFragcs.length;
          for (var i = tempFragcsLength - 1; i >= 0; i--) {
            tempFrags.push (tempFragcs[i]);
          }
        }
      }
    }
    return result;
  };

/* ***** BEGIN LICENSE BLOCK *****
 * Copyright 2005 Wakaba <w@suika.fam.cx>.  All rights reserved.
 *
 * This program is free software; you can redistribute it and/or 
 * modify it under the same terms as Perl itself.
 *
 * Alternatively, the contents of this file may be used 
 * under the following terms (the "MPL/GPL/LGPL"), 
 * in which case the provisions of the MPL/GPL/LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of the MPL/GPL/LGPL, and not to allow others to
 * use your version of this file under the terms of the Perl, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the MPL/GPL/LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the Perl or the MPL/GPL/LGPL.
 *
 * "MPL/GPL/LGPL":
 *
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/>
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is SuikaURNResolver code.
 *
 * The Initial Developer of the Original Code is Wakaba.
 * Portions created by the Initial Developer are Copyright (C) 2005
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Wakaba <w@suika.fam.cx>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the LGPL or the GPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */
