// vim: ts=2 sw=2 expandtab ai
//
// BEGIN FLOCK GPL
//
// Copyright Flock Inc. 2005-2009
// http://flock.com
//
// This file may be used under the terms of the
// GNU General Public License Version 2 or later (the "GPL"),
// http://www.gnu.org/licenses/gpl.html
//
// 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.
//
// END FLOCK GPL
//

// Ian's second Object RDF Mapping
//   look at cooptest.js

// TODO:
//  * lazy loading of properties (with caching)
//  * too much is done in ctor - it should be more static - things put into
//  the protototype.
//  * real exception class, not strings

const CC = Components.classes;
const CI = Components.interfaces;

const IOS = CC["@mozilla.org/network/io-service;1"]
            .getService(CI.nsIIOService);
const RDFS = CC["@mozilla.org/rdf/rdf-service;1"]
             .getService(CI.nsIRDFService);
const RDFCU = CC["@mozilla.org/rdf/container-utils;1"]
              .getService(CI.nsIRDFContainerUtils);

const FLOCK_NS = "http://flock.com/rdf#";
const COOP_TYPE = RDFS.GetResource(FLOCK_NS + "CoopType");


var coop_logger = CC["@flock.com/logger;1"].createInstance(CI.flockILogger);
coop_logger.init("coop");

function CoopType(aORM, aType, aFields, aFieldTypes, aFieldDefaults,
                  aFieldMultiple, aContainer, aContainerName, aContainerType)
{
  this.mORM = aORM;
  this.mType = aType;
  this.mTypeResource = RDFS.GetResource(aType);
  this.fields = aFields;
  this.field_types = aFieldTypes;
  this.field_defaults = aFieldDefaults;
  this.field_multiple = aFieldMultiple;
  this.container = aContainer;
  this.container_name = aContainerName;
  this.container_type = aContainerType;
  this.baseClasses = [];
  this.destroyNotifiers = [];

  var ormtype = this;

  this.ctor = function coop_obj_ctor(uri, field_values,
                                     resource, disableAccessors) {
    var type = ormtype;
    var instance = this;
    if (!uri || typeof uri != 'string') {
      if (uri) {
        // case for constructing with only an array of field values.
        field_values = uri;
        uri = null;
      }
      var get_id = type.mORM.mTypes[type.mType].ctor.get_id;
      if (get_id && field_values) {
        uri = get_id(field_values);
      }

      if (!uri) {
        uri = RDFS.GetAnonymousResource().ValueUTF8;
      }
    }
    // JMC - Assert the CoopType first, so that observers responding to the other
    // types can perform a coop "get"
    var firstTime = false;
    if (!resource) {
      resource = RDFS.GetResource(uri);
      if (!type.mORM.datasource.GetTarget(resource, COOP_TYPE, true)) {
        type.mORM.datasource.Assert(resource, COOP_TYPE, 
                                   type.mTypeResource, true);
        firstTime = true;
      }
    }
    this.fls = CC["@flock.com/rdf-source-utils;1"]
                .createInstance(CI.flockIRDFSourceUtils);
    this.fls.init(type.mORM.datasource, resource);

    if (type.container) {
      var container = null;

      // initialise the container
      if (type.container &&
          !RDFCU.IsContainer(type.mORM.datasource, resource)) {
        switch (type.container) {
          case 'Seq':
            RDFCU.MakeSeq(type.mORM.datasource, resource);
            break;
          case 'Bag':
            RDFCU.MakeBag(type.mORM.datasource, resource);
            break;
          case 'Alt':
            RDFCU.MakeAlt(type.mORM.datasource, resource);
            break;
          default:
            throw 'invalid container type';
            break;
        }
      }

      // provide an API
      function container_enumerator() { }
      container_enumerator.init_container = function() {
        container = CC["@mozilla.org/rdf/container;1"]
                    .createInstance(CI.flockIRDFContainer);
        container.Init(type.mORM.datasource, resource);
      }
      container_enumerator.count = function() {
        if (!container) this.init_container();
        return container.GetCount();
      }
      container_enumerator.add = function container_enum_add(child) {
        if (!container) this.init_container();
        container.AppendElement(child.__Coop_resource);
      }
      container_enumerator.insertSortedOn = function(aDateCriteria, 
                                                     aChild, aMaxCount) {
        return instance.fls.insertSortedOn(aDateCriteria, 
                                           aChild.__Coop_resource, aMaxCount);
      }
      container_enumerator.insertAt = function container_enum_insertAt(child, position) {
        if (!container) this.init_container();
        //  /!\ the first element is 1, not 0 /!\
        container.InsertElementAt(child.__Coop_resource, position, true);
      }
      container_enumerator.remove = function container_enum_remove(child) {
        if (!container) this.init_container();
        container.RemoveElement(child.__Coop_resource, false);
      }
      container_enumerator.enumerate = function container_enum_enumerate() {
        if (!container) this.init_container();
        var enumerator = {
          hasMoreElements: function container_enum_hasMoreElements() {
            return this.elements.hasMoreElements();
          },
          getNext: function container_enum_getNext() {
            var el = this.elements.getNext();
            el.QueryInterface(CI.nsIRDFResource);
            return type.mORM.get_from_resource(el);
          }
        }
        enumerator.elements = container.GetElements();
        return enumerator;
      }
      container_enumerator.enumerateBackwards = function container_enum_enumBackwards() {
        if (!container) this.init_container();
        var enumerator = {
          hasMoreElements: function container_backEnum_hasMoreElements() {
            return this.elements.hasMoreElements();
          },
          getNext: function container_backEnum_getNext() {
            var el = this.elements.getNext();
            el.QueryInterface(CI.nsIRDFResource);
            return type.mORM.get_from_resource(el);
          }
        }
        enumerator.elements = container.GetElementsBackwards();
        return enumerator;
      }
      container_enumerator.indexOf = function container_enum_indexOf(child) {
        if (!container) this.init_container();
        return container.IndexOf(child.__Coop_resource);
      }
      container_enumerator.has = function container_enum_has(child) {
        if (!container) this.init_container();
        return container.IndexOf(child.__Coop_resource) >= 0;
      }
      container_enumerator.addOnce = function container_enum_addOnce(child) {
        if (!container) this.init_container();
        if (this.indexOf(child) < 0) this.add(child);
      }
      container_enumerator.empty = function container_empty() {
        instance.fls.emptyContainer();
      }

      instance[type.container_name] = container_enumerator;
    }

    // set up the fields

    function set_up_field(f) {
      if (type.field_multiple[f]) {
        var field_type = type.field_types[f] || Coop.string;
        var field_rsrc = type.fields[f];
        var field_enumerator = function field_enum_ctor() { }
        if (!type.read_only) {
          field_enumerator.add = function field_enum_add(child) {
            type.mORM.datasource.Assert(resource, field_rsrc, 
                field_type.toRdf(child, type.mORM), true);
          }
          field_enumerator.remove = function field_enum_remove(child) {
            type.mORM.datasource.Unassert(resource, field_rsrc,
                field_type.toRdf(child, type.mORM));
          }
          field_enumerator.addOnce = function field_enum_addOnce(child) {
             if (!type.mORM.datasource.HasAssertion(resource,
                                                    field_rsrc,
                                                    field_type.toRdf(child, type.mORM),
                                                    true))
             {
               this.add(child);
             }
          }
          field_enumerator.has = function field_enum_has(child) {
            return type.mORM.datasource.HasAssertion(resource, field_rsrc,
              field_type.toRdf(child, type.mORM), true);
          }
          field_enumerator.removeAll = function field_enum_removeAll() {
            instance.fls.deleteAllTargets(field_rsrc);
          }
        }
        field_enumerator.enumerate = function field_enum_enumerate() {
          var elements = type.mORM.datasource.GetTargets(resource,
              field_rsrc, true);
          var enumerator = {
            hasMoreElements: function field_enum_hasMoreElements() {
              return elements.hasMoreElements();
            },
            getNext: function field_enum_getNext() {
              var el = elements.getNext();
              return field_type.toJs(el, type.mORM);
            }
          }
          return enumerator;
        }
        instance.__defineGetter__(f, function() {
          return field_enumerator;
        });
        instance.__defineSetter__(f, function(v) {
            throw "can't set multiple property: "+f+' to: '+v;
        });
        // FIXME: handle read-only
      } else {
        if (!type.read_only) {
          instance.__defineSetter__(f, function instance_field_setter(v) {
            this.set(f, v, false);
          });
        } else {
          instance.__defineSetter__(f, function instance_field_setter() {
            throw "can't set property on read-only object";
          });
        }
        instance.__defineGetter__(f, function instance_field_getter() {
          return this.get(f, false);
        });
      }
    }

    if (!type.read_only) {
      instance.destroy = function coop_obj_destroy() {
        for each (var func in type.destroyNotifiers) {
          func(this, type.mORM);
        }
        this.fls.destroy();
      }
    }

    instance.isA = function coop_obj_isA(aCoopType) { return type.isA(aCoopType);  }
    instance.isInstanceOf = function coop_obj_isInstanceOf(aCoopType) { return type.isInstanceOf(aCoopType); }
    // fixme: shouldn't this just be a read-only field?
    instance.id = function coop_obj_id() { return uri; }
    instance.changeId = function coop_obj_changeId(aNewURI) {
      // First make sure aNewURI doesn't already exist
      var newResource = RDFS.GetResource(aNewURI);
      var preexistingArcsIn = type.mORM.datasource.ArcLabelsIn(newResource);
      var alreadyInUse = (preexistingArcsIn &&
                          preexistingArcsIn.hasMoreElements());
      if (!alreadyInUse) {
        var preexistingArcsOut = type.mORM.datasource.ArcLabelsOut(newResource);
        alreadyInUse = (preexistingArcsOut &&
                        preexistingArcsOut.hasMoreElements());
      }
      if (alreadyInUse) {
        throw "The new URI '" + aNewURI + "' is already in use";
      }
      // Change all arcs in to point to the new resource
      var arcs = type.mORM.datasource.ArcLabelsIn(resource);
      while (arcs && arcs.hasMoreElements()) {
        var prop = arcs.getNext();
        if (prop && prop.QueryInterface) {
          prop = prop.QueryInterface(CI.nsIRDFResource);
        }
        var sources = type.mORM.datasource.GetSources(prop, resource, true);
        while (sources && sources.hasMoreElements()) {
          var source = sources.getNext();
          if (source && source.QueryInterface) {
            source = source.QueryInterface(CI.nsIRDFResource);
            type.mORM.datasource.Change(source, prop, resource, newResource);
          }
        }
      }
      // Change all arcs out to originate from the new resource
      arcs = type.mORM.datasource.ArcLabelsOut(resource);
      while (arcs && arcs.hasMoreElements()) {
        var prop = arcs.getNext()
        if (prop && prop.QueryInterface) {
          prop = prop.QueryInterface(CI.nsIRDFResource);
        }
        var targets = type.mORM.datasource.GetTargets(resource, prop, true);
        while (targets && targets.hasMoreElements()) {
          var target = targets.getNext();
          if (target && target.QueryInterface) {
            target = target.QueryInterface(CI.nsIRDFNode);
            type.mORM.datasource.Unassert(resource, prop, target);
            type.mORM.datasource.Assert(newResource, prop, target, true);
          }
        }
      }
      // Update the properties of the coop object
      resource = newResource;
      uri = aNewURI;
    }
    instance.resource = function coop_obj_resource() { return resource; }
    instance.getParent = function coop_obj_getParent() {
      var parent = this.fls.parent;
      return parent ? type.mORM.get_from_resource(parent) : null;
    }
    instance.set = function coop_obj_set(aFieldName, aFieldValue, aCustom) {
      var rdf_value = null;
      if (aFieldValue != null) {
        // find the data type - explicit or string by default
        var field_type = type.field_types[aFieldName] || Coop.string;
        rdf_value = field_type.toRdf(aFieldValue);
      }
      var field_pred = aCustom ? FLOCK_NS + aFieldName
                               : type.fields[aFieldName].ValueUTF8;
      this.fls.setTarget(field_pred, rdf_value);
    }
    instance.get = function coop_obj_get(aFieldName, aCustom) {
      // try to get a value from RDF
      var field_pred = aCustom ? RDFS.GetResource(FLOCK_NS + aFieldName)
                               : type.fields[aFieldName];
      var target = type.mORM.datasource.GetTarget(resource, field_pred, true);
      if (target == null) {
        return null;
      } else {
        // find the data type - explicit or string by default
        var field_type = type.field_types[aFieldName] || Coop.string;
        return field_type.toJs(target, type.mORM);
      }
    }

    instance.getParents = function coop_obj_getParents() {
      var result = [];
      var arcs = type.mORM.datasource.ArcLabelsIn(resource);
      while (arcs && arcs.hasMoreElements()) {
        var testresource = arcs.getNext();
        if (testresource && testresource.QueryInterface)
          testresource = testresource.QueryInterface(CI.nsIRDFResource);
        if (testresource && testresource.QueryInterface && RDFCU.IsOrdinalProperty(testresource)) {
          var sources = type.mORM.datasource.GetSources(testresource, resource, true);
          while (sources && sources.hasMoreElements()) {
            var source = sources.getNext().QueryInterface(CI.nsIRDFResource);
            var finalresource = type.mORM.get_from_resource(source);
            result.push(finalresource);
          }
        }
      }
      return result;
    }
    instance.__Coop_resource = resource;
    if (!disableAccessors) {
      for (var f in type.fields) {
        set_up_field(f);
      }
    }

    if (field_values || firstTime) {
      for (var f in type.fields) {
        if (!field_values || field_values[f] == null) {
          // Make sure default value ends up in here
          if (type.field_defaults[f] != null && this.get(f) == null) {
            this.set(f, type.field_defaults[f], false);
          }
        } else {
          if (type.field_multiple[f]) {
            // FIXME: initializer for a multiple field? eh?
            throw 'multiple field initializer not supported yet';
          }
          this.set(f, field_values[f], false);
        }
      }
    }

    if (firstTime && instance.onCreate) instance.onCreate();
  }

  this.ctor.all_ids = function all_ids() {
    // FIXME: deal with inheritance
    var sources = ormtype.mORM.datasource
                         .GetSources(COOP_TYPE, ormtype.mTypeResource, true);
    var results = [];
    while (sources.hasMoreElements()) {
      var source = sources.getNext().QueryInterface(CI.nsIRDFResource);
      results.push(source);
    }
    return {
      hasMoreElements: function coop_obj_allIds_hasMoreElements() { return results.length > 0; },
      getNext: function coop_obj_allIds_getNext() { return results.shift(); }
    };
  }

  this.ctor.all = function all() {
    // FIXME: deal with inheritance
    var ids = ormtype.ctor.all_ids();

    return {
      hasMoreElements: function coop_obj_all_hasMoreElements() { return ids.hasMoreElements(); },
      getNext: function coop_obj_all_getNext() { return ormtype.mORM.get_from_resource(ids.getNext()); }
    };
  }


  this.ctor.find = function find(aQuery) {
    // FIXME: special queries for rdfsql - faster pussycat, search! search!
    // FIXME: we need to be able to order the keys to match in a query

    var realResults = [];
    var firstprop = true;
    for (var propname in aQuery) {
      var results = [];
        // coop_logger.info("Coop Find: looking at property" + propname + " for value of " + aQuery[propname]);
      var type = ormtype.field_types[propname] || Coop.string;
      var sources = ormtype.mORM.datasource.GetSources(ormtype.fields[propname],
        type.toRdf(aQuery[propname], ormtype.mORM), true);
      while (sources.hasMoreElements()) {
        var source = sources.getNext().QueryInterface(CI.nsIRDFResource);
        if (!ormtype.mORM.datasource.HasAssertion(source, COOP_TYPE,
              ormtype.mTypeResource, true)) {
          continue;
        }
        results.push(source.ValueUTF8);
      }
      if (firstprop) {
        realResults = results;
        firstprop = false;
      }
      // JMC - Loop backwards through the realResults,
      // if they don't exist in results, then pop them
      var interSection = [];
      for (var i=0 ; i < realResults.length; i++)
      {
        if (results.indexOf(realResults[i]) > -1)
        {
          interSection.push(realResults[i]);
        }
      }
      realResults = interSection;
    }
    return realResults;
  }

  this.ctor.exists = function exists(aURN) {
    return ormtype.mORM.datasource.HasAssertion(RDFS.GetResource(aURN),
        COOP_TYPE, ormtype.mTypeResource, true);
  }

  this.ctor._get = function _get(aURI, aResource, disableAccessors) {
    if (!aURI) {
      aURI = aResource.ValueUTF8;
      if (!aURI) {
        throw "call QueryInterface before calling get()";
      }
    }
    return new this(aURI, null, aResource, disableAccessors);
  }

  this.ctor.get = function get(aURI, aResource, disableAccessors) {
    if (!aResource) {
      aResource = RDFS.GetResource(aURI);
    }
    var type_target = this.ds.GetTarget(aResource, COOP_TYPE, true);
    if (!type_target) {
      var fls = CC["@flock.com/rdf-source-utils;1"]
                .createInstance(CI.flockIRDFSourceUtils);
      fls.init(this.ds, aResource);
      var p = fls.parent;
      if (p) {
        var msg = aResource.ValueUTF8 + " from " + p.ValueUTF8;
        coop_logger.error("**ERROR** Dangling element: " + msg);
      }
      return null;
    } else if (type_target.ValueUTF8 != this.mType) {
      return null;
    }
    return this._get(aURI, aResource, disableAccessors);
  }

  this.ctor.add_destroy_notifier = function add_destroy_notifier(aNotifyFunc) {
    ormtype.add_destroy_notifier(aNotifyFunc);
  }

  this.ctor.remove_destroy_notifier = function remove_destroy_notifier(aNotifyFunc) {
    ormtype.remove_destroy_notifier(aNotifyFunc);
  }

  this.ctor.mType = this.mType; // JMC - need this later
  this.ctor.ds = ormtype.mORM.datasource;
}

CoopType.prototype.add_destroy_notifier = function CoopType_add_destroy_notifier(aNotifyFunc) {
  function hasFunc(element, index, array) {
    return element == aNotifyFunc;
  }
  if (!this.destroyNotifiers.some(hasFunc))
    this.destroyNotifiers.push(aNotifyFunc);
}

CoopType.prototype.remove_destroy_notifier = function CoopType_remove_destroy_notifier(aNotifyFunc) {
  function keepFunc(element, index, array) {
    return element != aNotifyFunc;
  }
  this.destroyNotifiers = this.destroyNotifiers.filter(keepFunc);
}

// aCoopType is an object that is the ctor of the CoopType
CoopType.prototype.isA = function CoopType_isA(aCoopType) {
 return aCoopType == this.ctor;
}

CoopType.prototype.isInstanceOf = function CoopType_isInstanceOf(aCoopType) {
  if (this.isA(aCoopType)) return true;
  if (aCoopType.mType && this.baseClasses.indexOf(aCoopType.mType) > -1) return true;
  return false;
}

function Coop(aDataSource, aSchema, aDefaultProperties) {
  this.datasource = aDataSource;
  this.mTypes = {};
  if (aDefaultProperties) {
      this.mDefaultType = new CoopType(this, 'urn:coop:internal:default',
          aDefaultProperties.fields, aDefaultProperties.field_types,
          aDefaultProperties.field_defaults, aDefaultProperties.field_multiple,
          aDefaultProperties.container, aDefaultProperties.container_name,
          aDefaultProperties.container_type);
  } else {
    this.mDefaultType = null;
  }
  if (aSchema) {
    this.addSchema(aSchema);
  }
}
Coop.prototype.addSchema = function (aSchema) {
  var loader = CC["@mozilla.org/moz/jssubscript-loader;1"]
               .getService(CI.mozIJSSubScriptLoader);
  var context = {Coop: Coop, coop: this};
  loader.loadSubScript(aSchema, context);
}
Coop.prototype.add_type = function Coop_add_type(aType, aProperties) {
  var inherits_from = aProperties['inherits_from'];
  if (inherits_from) {
    inherits_from = this.mTypes[inherits_from];
  } else if (this.mDefaultType) {
    inherits_from = this.mDefaultType;
  }

  var k;

  var fields = {};
  if (inherits_from) {
    for (k in inherits_from.fields) {
      fields[k] = inherits_from.fields[k];
    }
  }
  if (aProperties.fields) {
    for (k in aProperties.fields) {
      fields[k] = RDFS.GetResource(aProperties.fields[k]);
    }
  }

  var field_types = {};
  if (inherits_from) {
    for (k in inherits_from.field_types) {
      field_types[k] = inherits_from.field_types[k];
    }
  }
  if (aProperties.field_types) {
    for (k in aProperties.field_types) {
      field_types[k] = aProperties.field_types[k];
    }
  }

  var field_defaults = {};
  if (inherits_from) {
    for (k in inherits_from.field_defaults) {
      field_defaults[k] = inherits_from.field_defaults[k];
    }
  }
  if (aProperties.field_defaults) {
    for (k in aProperties.field_defaults) {
      field_defaults[k] = aProperties.field_defaults[k];
    }
  }

  var field_multiple = {};
  if (inherits_from) {
    for (k in inherits_from.field_multiple) {
      field_multiple[k] = inherits_from.field_multiple[k];
    }
  }
  if (aProperties.field_multiple) {
    for (k in aProperties.field_multiple) {
      field_multiple[k] = aProperties.field_multiple[k];
    }
  }

  var container = aProperties.container || aProperties.hashtable ||
      (inherits_from && inherits_from.container);
  var container_type = "";
  if (container) {
    if (aProperties.hashtable) {
      container_type = "hashtable";
    } else {
      container_type = "container";
    }
  }

  var container_name = null;
  if (aProperties.container_name) {
    container_name = aProperties.container_name;
  } else if (inherits_from && inherits_from.container_name) {
    container_name = inherits_from.container_name;
  } else {
    container_name = 'children';
  }

/*
  var constructor = null;
  if (aProperties.constructor) {
    constructor = aProperties.constructor;
  } else if (inherits_from && inherits_from.constructor) {
    constructor = inherits_from.constructor;
  }
  */

  this.mTypes[aType] =
      new CoopType(this, aType,
                   fields, field_types, field_defaults, field_multiple,
                   container, container_name, container_type);
  
  // JMC - Move this into the ctor with everything else
  if (aProperties['inherits_from']) {
    this.mTypes[aType].baseClasses.push(aProperties['inherits_from']);
    for (basetype in inherits_from.baseClasses) {
      this.mTypes[aType].baseClasses.push(inherits_from.baseClasses[basetype]);
    }
  }

  return this.mTypes[aType].ctor;
}
Coop.prototype.get_from_resource =
function Coop_get_from_resource(aResource, aURI, disableAccessors) {
  var type_target = this.datasource.GetTarget(aResource, COOP_TYPE, true);
  if (!type_target) {
    var fls = CC["@flock.com/rdf-source-utils;1"]
              .createInstance(CI.flockIRDFSourceUtils);
    fls.init(this.datasource, aResource);
    var p = fls.parent;
    if (p) {
      var msg = aResource.ValueUTF8 + " from " + p.ValueUTF8;
      coop_logger.error("**ERROR** Dangling element: " + msg);
    }
    return null;
  }
  var coopType = this.mTypes[type_target.ValueUTF8];
  return coopType.ctor._get(aURI, aResource, disableAccessors);
}
Coop.prototype.get = function Coop_get(aURI, disableAccessors) {
  if (!aURI) {
    return null;
  }
  var res = RDFS.GetResource(aURI);
  return this.get_from_resource(res, aURI, disableAccessors);
}

// standard types
const INVALID_XML_CHARACTERS = /[^\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD]/g;
Coop.string = {
  toJs: function string_toJS(node, orm) {
    return node.QueryInterface(CI.nsIRDFLiteral).Value;
  },
  toRdf: function string_toRdf(value, orm) {
    if (value == null) {
      value = "";
    }
    value = value.toString(); // force it to a string
    return RDFS.GetLiteral(value.replace(INVALID_XML_CHARACTERS, ''));
  }
}
Coop.resource = {
  toJs: function resource_toJS(node, orm) {
    return node.QueryInterface(CI.nsIRDFResource).ValueUTF8;
  },
  toRdf: function resource_toRdf(value, orm) {
    return RDFS.GetResource(value);
  }
}
Coop.date = {
  toJs: function date_toJs(node, orm) {
    return new Date(node.QueryInterface(CI.nsIRDFDate).Value / 1000);
  },
  // FIXME: I should not need this 1000* here, but for some reason I get the wrong date
  // without it
  toRdf: function date_toRdf(value, orm) {
    return RDFS.GetDateLiteral(1000 * value.getTime());
  }
}
Coop.boolean = {
  toJs: function boolean_toJs(node, orm) {
    return (node.QueryInterface(CI.nsIRDFLiteral).Value == "true");
  },
  toRdf: function boolean_toRdf(value, orm) {
    return RDFS.GetLiteral(value ? "true" : "false");
  }
}
Coop.integer = {
  toJs: function integer_toJs(node, orm) {
    return node.QueryInterface(CI.nsIRDFInt).Value;
  },
  toRdf: function integer_toRdf(value, orm) {
    return RDFS.GetIntLiteral(value);
  }
}
Coop.stringinteger = {
  toJs: function stringinteger_toJs(node, orm) {
    return parseInt(node.QueryInterface(CI.nsIRDFLiteral).Value);
  },
  toRdf: function stringinteger_toRdf(value, orm) {
    return RDFS.GetLiteral(value);
  }
}
Coop.object = {
  toJs: function object_toJs(node, orm) {
    return orm.get_from_resource(node.QueryInterface(CI.nsIRDFResource));
  },
  toRdf: function object_toRdf(value, orm) {
    return value.__Coop_resource;
  }
}
// TODO: implement compare and toString
