/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * 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 Mozilla.
 * 
 * The Initial Developer of the Original Code is Netscape
 * Communications.  Portions created by Netscape Communications are
 * Copyright (C) 2001 by Netscape Communications.  All
 * Rights Reserved.
 * 
 * Contributor(s): 
 *   Vidur Apparao <vidur@netscape.com> (original author)
 */

#include "nsSchemaPrivate.h"
#include "nsSchemaLoader.h"

// content includes
#include "nsIContent.h"
#include "nsINameSpaceManager.h"
#include "nsINodeInfo.h"
#include "nsIDOMDocument.h"

// loading includes
#include "nsIXMLHttpRequest.h"
#include "nsIDOMEvent.h"
#include "nsIDOMEventListener.h"
#include "nsIDOMEventTarget.h"
#include "nsNetUtil.h"

// string includes
#include "nsReadableUtils.h"

// XPConnect includes
#include "nsIXPConnect.h"
#include "nsIScriptSecurityManager.h"
#include "nsICodebasePrincipal.h"

// XPCOM includes
#include "nsIServiceManager.h"
#include "nsIComponentManager.h"

////////////////////////////////////////////////////////////
//
// nsSchemaAtoms implementation
//
////////////////////////////////////////////////////////////

// Statics
nsIAtom* nsSchemaAtoms::sAnyType_atom = nsnull;
nsIAtom* nsSchemaAtoms::sString_atom = nsnull;
nsIAtom* nsSchemaAtoms::sNormalizedString_atom = nsnull;
nsIAtom* nsSchemaAtoms::sToken_atom = nsnull;
nsIAtom* nsSchemaAtoms::sByte_atom = nsnull;
nsIAtom* nsSchemaAtoms::sUnsignedByte_atom = nsnull;
nsIAtom* nsSchemaAtoms::sBase64Binary_atom = nsnull;
nsIAtom* nsSchemaAtoms::sHexBinary_atom = nsnull;
nsIAtom* nsSchemaAtoms::sInteger_atom = nsnull;
nsIAtom* nsSchemaAtoms::sPositiveInteger_atom = nsnull;
nsIAtom* nsSchemaAtoms::sNegativeInteger_atom = nsnull;
nsIAtom* nsSchemaAtoms::sNonnegativeInteger_atom = nsnull;
nsIAtom* nsSchemaAtoms::sNonpositiveInteger_atom = nsnull;
nsIAtom* nsSchemaAtoms::sInt_atom = nsnull;
nsIAtom* nsSchemaAtoms::sUnsignedInt_atom = nsnull;
nsIAtom* nsSchemaAtoms::sLong_atom = nsnull;
nsIAtom* nsSchemaAtoms::sUnsignedLong_atom = nsnull;
nsIAtom* nsSchemaAtoms::sShort_atom = nsnull;
nsIAtom* nsSchemaAtoms::sUnsignedShort_atom = nsnull;
nsIAtom* nsSchemaAtoms::sDecimal_atom = nsnull;
nsIAtom* nsSchemaAtoms::sFloat_atom = nsnull;
nsIAtom* nsSchemaAtoms::sDouble_atom = nsnull;
nsIAtom* nsSchemaAtoms::sBoolean_atom = nsnull;
nsIAtom* nsSchemaAtoms::sTime_atom = nsnull;
nsIAtom* nsSchemaAtoms::sDateTime_atom = nsnull;
nsIAtom* nsSchemaAtoms::sDuration_atom = nsnull;
nsIAtom* nsSchemaAtoms::sDate_atom = nsnull;
nsIAtom* nsSchemaAtoms::sGMonth_atom = nsnull;
nsIAtom* nsSchemaAtoms::sGYear_atom = nsnull;
nsIAtom* nsSchemaAtoms::sGYearMonth_atom = nsnull;
nsIAtom* nsSchemaAtoms::sGDay_atom = nsnull;
nsIAtom* nsSchemaAtoms::sGMonthDay_atom = nsnull;
nsIAtom* nsSchemaAtoms::sName_atom = nsnull;
nsIAtom* nsSchemaAtoms::sQName_atom = nsnull;
nsIAtom* nsSchemaAtoms::sNCName_atom = nsnull;
nsIAtom* nsSchemaAtoms::sAnyUri_atom = nsnull;
nsIAtom* nsSchemaAtoms::sLanguage_atom = nsnull;
nsIAtom* nsSchemaAtoms::sID_atom = nsnull;
nsIAtom* nsSchemaAtoms::sIDREF_atom = nsnull;
nsIAtom* nsSchemaAtoms::sIDREFS_atom = nsnull;
nsIAtom* nsSchemaAtoms::sENTITY_atom = nsnull;
nsIAtom* nsSchemaAtoms::sENTITIES_atom = nsnull;
nsIAtom* nsSchemaAtoms::sNOTATION_atom = nsnull;
nsIAtom* nsSchemaAtoms::sNMTOKEN_atom = nsnull;
nsIAtom* nsSchemaAtoms::sNMTOKENS_atom = nsnull;

nsIAtom* nsSchemaAtoms::sElement_atom = nsnull;
nsIAtom* nsSchemaAtoms::sModelGroup_atom = nsnull;
nsIAtom* nsSchemaAtoms::sAny_atom = nsnull;
nsIAtom* nsSchemaAtoms::sAttribute_atom = nsnull;
nsIAtom* nsSchemaAtoms::sAttributeGroup_atom = nsnull;
nsIAtom* nsSchemaAtoms::sSimpleType_atom = nsnull;
nsIAtom* nsSchemaAtoms::sComplexType_atom = nsnull;
nsIAtom* nsSchemaAtoms::sSimpleContent_atom = nsnull;
nsIAtom* nsSchemaAtoms::sComplexContent_atom = nsnull;
nsIAtom* nsSchemaAtoms::sAll_atom = nsnull;
nsIAtom* nsSchemaAtoms::sChoice_atom = nsnull;
nsIAtom* nsSchemaAtoms::sSequence_atom = nsnull;
nsIAtom* nsSchemaAtoms::sAnyAttribute_atom = nsnull;
nsIAtom* nsSchemaAtoms::sRestriction_atom = nsnull;
nsIAtom* nsSchemaAtoms::sExtension_atom = nsnull;
nsIAtom* nsSchemaAtoms::sAnnotation_atom = nsnull;
nsIAtom* nsSchemaAtoms::sList_atom = nsnull;
nsIAtom* nsSchemaAtoms::sUnion_atom = nsnull;

nsIAtom* nsSchemaAtoms::sMinExclusive_atom = nsnull;
nsIAtom* nsSchemaAtoms::sMinInclusive_atom = nsnull;
nsIAtom* nsSchemaAtoms::sMaxExclusive_atom = nsnull;
nsIAtom* nsSchemaAtoms::sMaxInclusive_atom = nsnull;
nsIAtom* nsSchemaAtoms::sTotalDigits_atom = nsnull;
nsIAtom* nsSchemaAtoms::sFractionDigits_atom = nsnull;
nsIAtom* nsSchemaAtoms::sLength_atom = nsnull;
nsIAtom* nsSchemaAtoms::sMinLength_atom = nsnull;
nsIAtom* nsSchemaAtoms::sMaxLength_atom = nsnull;
nsIAtom* nsSchemaAtoms::sEnumeration_atom = nsnull;
nsIAtom* nsSchemaAtoms::sWhiteSpace_atom = nsnull;
nsIAtom* nsSchemaAtoms::sPattern_atom = nsnull;

void
nsSchemaAtoms::CreateSchemaAtoms()
{
  sAnyType_atom = NS_NewAtom("anyType");
  sString_atom = NS_NewAtom("string");
  sNormalizedString_atom = NS_NewAtom("normalizedString");
  sToken_atom = NS_NewAtom("token");
  sByte_atom = NS_NewAtom("byte");
  sUnsignedByte_atom = NS_NewAtom("unsignedByte");
  sBase64Binary_atom = NS_NewAtom("base64Binary");
  sHexBinary_atom = NS_NewAtom("hexBinary");
  sInteger_atom = NS_NewAtom("integer");
  sPositiveInteger_atom = NS_NewAtom("positiveInteger");
  sNegativeInteger_atom = NS_NewAtom("negativeInteger");
  sNonnegativeInteger_atom = NS_NewAtom("nonNegativeInteger");
  sNonpositiveInteger_atom = NS_NewAtom("nonPositiveInteger");
  sInt_atom = NS_NewAtom("int");
  sUnsignedInt_atom = NS_NewAtom("unsignedInt");
  sLong_atom = NS_NewAtom("long");
  sUnsignedLong_atom = NS_NewAtom("unsignedLong");
  sShort_atom = NS_NewAtom("short");
  sUnsignedShort_atom = NS_NewAtom("unsignedShort");
  sDecimal_atom = NS_NewAtom("decimal");
  sFloat_atom = NS_NewAtom("float");
  sDouble_atom = NS_NewAtom("double");
  sBoolean_atom = NS_NewAtom("boolean");
  sTime_atom = NS_NewAtom("time");
  sDateTime_atom = NS_NewAtom("dateTime");
  sDuration_atom = NS_NewAtom("duration");
  sDate_atom = NS_NewAtom("date");
  sGMonth_atom = NS_NewAtom("gMonth");
  sGYear_atom = NS_NewAtom("gYear");
  sGYearMonth_atom = NS_NewAtom("gYearMonth");
  sGDay_atom = NS_NewAtom("gDay");
  sGMonthDay_atom = NS_NewAtom("gMonthDay");
  sName_atom = NS_NewAtom("name");
  sQName_atom = NS_NewAtom("QName");
  sNCName_atom = NS_NewAtom("NCName");
  sAnyUri_atom = NS_NewAtom("anyUri");
  sLanguage_atom = NS_NewAtom("language");
  sID_atom = NS_NewAtom("ID");
  sIDREF_atom = NS_NewAtom("IDREF");
  sIDREFS_atom = NS_NewAtom("IDREFS");
  sENTITY_atom = NS_NewAtom("ENTITY");
  sENTITIES_atom = NS_NewAtom("ENTITIES");
  sNOTATION_atom = NS_NewAtom("NOTATION");
  sNMTOKEN_atom = NS_NewAtom("NMTOKEN");
  sNMTOKENS_atom = NS_NewAtom("NMTOKENS");

  sElement_atom = NS_NewAtom("element");
  sModelGroup_atom = NS_NewAtom("group");
  sAny_atom = NS_NewAtom("any");
  sAttribute_atom = NS_NewAtom("attribute");
  sAttributeGroup_atom = NS_NewAtom("attributeGroup");
  sSimpleType_atom = NS_NewAtom("simpleType");
  sComplexType_atom = NS_NewAtom("complexType");
  sSimpleContent_atom = NS_NewAtom("simpleContent");
  sComplexContent_atom = NS_NewAtom("complexContent");
  sAll_atom = NS_NewAtom("all");
  sChoice_atom = NS_NewAtom("choice");
  sSequence_atom = NS_NewAtom("sequence");
  sAnyAttribute_atom = NS_NewAtom("anyAttribute");
  sRestriction_atom = NS_NewAtom("restriction");
  sExtension_atom = NS_NewAtom("extension");
  sAnnotation_atom = NS_NewAtom("annotation");
  sList_atom = NS_NewAtom("list");
  sUnion_atom = NS_NewAtom("union");

  sMinExclusive_atom = NS_NewAtom("minExclusive");
  sMinInclusive_atom = NS_NewAtom("minInclusive");
  sMaxExclusive_atom = NS_NewAtom("maxExclusive");
  sMaxInclusive_atom = NS_NewAtom("maxInclusive");
  sTotalDigits_atom = NS_NewAtom("totalDigits");
  sFractionDigits_atom = NS_NewAtom("fractionDigits");
  sLength_atom = NS_NewAtom("length");
  sMinLength_atom = NS_NewAtom("minLength");
  sMaxLength_atom = NS_NewAtom("maxLength");
  sEnumeration_atom = NS_NewAtom("enumeration");
  sWhiteSpace_atom = NS_NewAtom("whiteSpace");
  sPattern_atom = NS_NewAtom("pattern");
}

void
nsSchemaAtoms::DestroySchemaAtoms()
{
  if (sAnyType_atom) {
    NS_RELEASE(sAnyType_atom);
    NS_RELEASE(sString_atom);
    NS_RELEASE(sNormalizedString_atom);
    NS_RELEASE(sToken_atom);
    NS_RELEASE(sByte_atom);
    NS_RELEASE(sUnsignedByte_atom);
    NS_RELEASE(sBase64Binary_atom);
    NS_RELEASE(sHexBinary_atom);
    NS_RELEASE(sInteger_atom);
    NS_RELEASE(sPositiveInteger_atom);
    NS_RELEASE(sNegativeInteger_atom);
    NS_RELEASE(sNonnegativeInteger_atom);
    NS_RELEASE(sNonpositiveInteger_atom);
    NS_RELEASE(sInt_atom);
    NS_RELEASE(sUnsignedInt_atom);
    NS_RELEASE(sLong_atom);
    NS_RELEASE(sUnsignedLong_atom);
    NS_RELEASE(sShort_atom);
    NS_RELEASE(sUnsignedShort_atom);
    NS_RELEASE(sDecimal_atom);
    NS_RELEASE(sFloat_atom);
    NS_RELEASE(sDouble_atom);
    NS_RELEASE(sBoolean_atom);
    NS_RELEASE(sTime_atom);
    NS_RELEASE(sDateTime_atom);
    NS_RELEASE(sDuration_atom);
    NS_RELEASE(sDate_atom);
    NS_RELEASE(sGMonth_atom);
    NS_RELEASE(sGYear_atom);
    NS_RELEASE(sGYearMonth_atom);
    NS_RELEASE(sGDay_atom);
    NS_RELEASE(sGMonthDay_atom);
    NS_RELEASE(sName_atom);
    NS_RELEASE(sQName_atom);
    NS_RELEASE(sNCName_atom);
    NS_RELEASE(sAnyUri_atom);
    NS_RELEASE(sLanguage_atom);
    NS_RELEASE(sID_atom);
    NS_RELEASE(sIDREF_atom);
    NS_RELEASE(sIDREFS_atom);
    NS_RELEASE(sENTITY_atom);
    NS_RELEASE(sENTITIES_atom);
    NS_RELEASE(sNOTATION_atom);
    NS_RELEASE(sNMTOKEN_atom);
    NS_RELEASE(sNMTOKENS_atom);
    
    NS_RELEASE(sElement_atom);
    NS_RELEASE(sModelGroup_atom);
    NS_RELEASE(sAny_atom);
    NS_RELEASE(sAttribute_atom);
    NS_RELEASE(sAttributeGroup_atom);
    NS_RELEASE(sSimpleType_atom);
    NS_RELEASE(sComplexType_atom);
    NS_RELEASE(sSimpleContent_atom);
    NS_RELEASE(sComplexContent_atom);
    NS_RELEASE(sAll_atom);
    NS_RELEASE(sChoice_atom);
    NS_RELEASE(sSequence_atom);
    NS_RELEASE(sAnyAttribute_atom);
    NS_RELEASE(sRestriction_atom);
    NS_RELEASE(sExtension_atom);
    NS_RELEASE(sAnnotation_atom);
    NS_RELEASE(sList_atom);
    NS_RELEASE(sUnion_atom);
    
    NS_RELEASE(sMinExclusive_atom);
    NS_RELEASE(sMinInclusive_atom);
    NS_RELEASE(sMaxExclusive_atom);
    NS_RELEASE(sMaxInclusive_atom);
    NS_RELEASE(sTotalDigits_atom);
    NS_RELEASE(sFractionDigits_atom);
    NS_RELEASE(sLength_atom);
    NS_RELEASE(sMinLength_atom);
    NS_RELEASE(sMaxLength_atom);
    NS_RELEASE(sEnumeration_atom);
    NS_RELEASE(sWhiteSpace_atom);
    NS_RELEASE(sPattern_atom);
  }
}

////////////////////////////////////////////////////////////
//
// LoadListener implementation
//
////////////////////////////////////////////////////////////

class LoadListener : public nsIDOMEventListener {
public:
  LoadListener(nsSchemaLoader* aLoader,
               nsISchemaLoadListener* aListener,
               nsIXMLHttpRequest* aRequest);
  virtual ~LoadListener();

  NS_DECL_ISUPPORTS
  NS_DECL_NSIDOMEVENTLISTENER

private:
  nsSchemaLoader* mLoader;
  nsCOMPtr<nsISchemaLoadListener> mListener;
  nsCOMPtr<nsIXMLHttpRequest> mRequest;
  nsString mURI;
};

LoadListener::LoadListener(nsSchemaLoader* aLoader,
                           nsISchemaLoadListener* aListener,
                           nsIXMLHttpRequest* aRequest) 
{
  NS_INIT_ISUPPORTS();
  mLoader = aLoader;
  NS_ADDREF(mLoader);
  mListener = aListener;
  mRequest = aRequest;
}
  
LoadListener::~LoadListener() 
{
  NS_IF_RELEASE(mLoader);
}

NS_IMPL_ISUPPORTS1(LoadListener, nsIDOMEventListener)

/* void handleEvent (in nsIDOMEvent event); */
NS_IMETHODIMP 
LoadListener::HandleEvent(nsIDOMEvent *event)
{
  nsresult rv;
  nsAutoString eventType;
  
  event->GetType(eventType);
  
  if (eventType.Equals(NS_LITERAL_STRING("load"))) {
    nsCOMPtr<nsIDOMDocument> document;
    nsCOMPtr<nsISchema> schema;
    
    rv = mRequest->GetResponseXML(getter_AddRefs(document));
    if (NS_SUCCEEDED(rv)) {
      nsCOMPtr<nsIDOMElement> element;
      
      document->GetDocumentElement(getter_AddRefs(element));
      if (element) {
        rv = mLoader->ProcessSchemaElement(element, getter_AddRefs(schema));
      }
      else {
        rv = NS_ERROR_SCHEMA_NOT_SCHEMA_ELEMENT;
      }
    }

    if (NS_SUCCEEDED(rv)) {
      mListener->OnLoad(schema);
    }
    else {
      mListener->OnError(rv, NS_LITERAL_STRING("Failure processing schema document"));
    }
  }
  else if (eventType.Equals(NS_LITERAL_STRING("error")) &&
           mListener) {
    mListener->OnError(NS_ERROR_SCHEMA_LOADING_ERROR, 
                       NS_LITERAL_STRING("Failure loading"));
  }
  NS_IF_RELEASE(mLoader);
  mListener = nsnull;
  mRequest = nsnull;

  return NS_OK;
}

////////////////////////////////////////////////////////////
//
// nsBuiltinSchemaCollection implementation
//
////////////////////////////////////////////////////////////
nsBuiltinSchemaCollection::nsBuiltinSchemaCollection()
{
  NS_INIT_ISUPPORTS();
  if (!nsSchemaAtoms::sAnyType_atom) {
    nsSchemaAtoms::CreateSchemaAtoms();
  }
}

nsBuiltinSchemaCollection::~nsBuiltinSchemaCollection()
{
  mBuiltinTypesHash.Reset();
  mSOAPTypeHash.Reset();
}

NS_IMPL_ISUPPORTS1(nsBuiltinSchemaCollection,
                   nsISchemaCollection)

/* nsISchema getSchema (in AString targetNamespace); */
NS_IMETHODIMP 
nsBuiltinSchemaCollection::GetSchema(const nsAReadableString & targetNamespace,
                                     nsISchema **_retval)
{
  NS_ENSURE_ARG_POINTER(_retval);
  *_retval = nsnull;
  return NS_ERROR_SCHEMA_UNKNOWN_TARGET_NAMESPACE;
}

/* nsISchemaElement getElement (in AString name, in AString namespace); */
NS_IMETHODIMP 
nsBuiltinSchemaCollection::GetElement(const nsAReadableString & aName, 
                                      const nsAReadableString & aNamespace, 
                                      nsISchemaElement **_retval)
{
  NS_ENSURE_ARG_POINTER(_retval);
  *_retval = nsnull;
  return NS_ERROR_FAILURE;
}

/* nsISchemaAttribute getAttribute (in AString name, in AString namespace); */
NS_IMETHODIMP 
nsBuiltinSchemaCollection::GetAttribute(const nsAReadableString & aName, 
                                        const nsAReadableString & aNamespace, 
                                        nsISchemaAttribute **_retval)
{
  NS_ENSURE_ARG_POINTER(_retval);
  *_retval = nsnull;
  return NS_ERROR_FAILURE;
}

static PRBool
IsSchemaNamespace(const nsAReadableString& aNamespace)
{
  if (aNamespace.Equals(NS_LITERAL_STRING(NS_SCHEMA_2001_NAMESPACE)) ||
      aNamespace.Equals(NS_LITERAL_STRING(NS_SCHEMA_1999_NAMESPACE))) {
    return PR_TRUE;
  }
  else {
    return PR_FALSE;
  }
}

static PRBool
IsSOAPNamespace(const nsAReadableString& aNamespace)
{
  if (aNamespace.Equals(NS_LITERAL_STRING(NS_SOAP_1_1_ENCODING_NAMESPACE)) ||
      aNamespace.Equals(NS_LITERAL_STRING(NS_SOAP_1_2_ENCODING_NAMESPACE))) {
    return PR_TRUE;
  }
  else {
    return PR_FALSE;
  }  
}

/* nsISchemaType getType (in AString name, in AString namespace); */
NS_IMETHODIMP 
nsBuiltinSchemaCollection::GetType(const nsAReadableString & aName, 
                                   const nsAReadableString & aNamespace, 
                                   nsISchemaType **_retval)
{
  if (IsSchemaNamespace(aNamespace)) {
    return GetBuiltinType(aName, aNamespace, _retval);
  }

  if (IsSOAPNamespace(aNamespace)) {
    return GetSOAPType(aName, aNamespace, _retval);
  }
  
  return NS_ERROR_SCHEMA_UNKNOWN_TYPE;
}

nsresult
nsBuiltinSchemaCollection::GetBuiltinType(const nsAReadableString& aName,
                                          const nsAReadableString& aNamespace,
                                          nsISchemaType** aType)
{
  nsresult rv = NS_OK;
  nsStringKey key(aName);
  nsCOMPtr<nsISupports> sup = dont_AddRef(mBuiltinTypesHash.Get(&key));
  if (sup) {
    rv = CallQueryInterface(sup, aType);
  }
  else {
    nsCOMPtr<nsIAtom> typeName = dont_AddRef(NS_NewAtom(aName));
    PRUint16 typeVal;
    if (typeName == nsSchemaAtoms::sAnyType_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_ANYTYPE;
    }
    else if (typeName == nsSchemaAtoms::sString_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_STRING;
    }
    else if (typeName == nsSchemaAtoms::sNormalizedString_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_NORMALIZED_STRING;
    }
    else if (typeName == nsSchemaAtoms::sToken_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_TOKEN;
    }
    else if (typeName == nsSchemaAtoms::sByte_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_BYTE;
    }
    else if (typeName == nsSchemaAtoms::sUnsignedByte_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDBYTE;
    }
    else if (typeName == nsSchemaAtoms::sBase64Binary_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_BASE64BINARY;
    }
    else if (typeName == nsSchemaAtoms::sHexBinary_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_HEXBINARY;
    }
    else if (typeName == nsSchemaAtoms::sInteger_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_INTEGER;
    }
    else if (typeName == nsSchemaAtoms::sPositiveInteger_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_POSITIVEINTEGER;
    }
    else if (typeName == nsSchemaAtoms::sNegativeInteger_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_NEGATIVEINTEGER;
    }
    else if (typeName == nsSchemaAtoms::sNonnegativeInteger_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_NONNEGATIVEINTEGER;
    }
    else if (typeName == nsSchemaAtoms::sNonpositiveInteger_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_NONPOSITIVEINTEGER;
    }
    else if (typeName == nsSchemaAtoms::sInt_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_INT;
    }
    else if (typeName == nsSchemaAtoms::sUnsignedInt_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDINT;
    }
    else if (typeName == nsSchemaAtoms::sLong_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_LONG;
    }
    else if (typeName == nsSchemaAtoms::sUnsignedLong_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDLONG;
    }
    else if (typeName == nsSchemaAtoms::sShort_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_SHORT;
    }
    else if (typeName == nsSchemaAtoms::sUnsignedShort_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDSHORT;
    }
    else if (typeName == nsSchemaAtoms::sDecimal_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_DECIMAL;
    }
    else if (typeName == nsSchemaAtoms::sFloat_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_FLOAT;
    }
    else if (typeName == nsSchemaAtoms::sDouble_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_DOUBLE;
    }
    else if (typeName == nsSchemaAtoms::sBoolean_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_BOOLEAN;
    }
    else if (typeName == nsSchemaAtoms::sTime_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_TIME;
    }
    else if (typeName == nsSchemaAtoms::sDateTime_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_DATETIME;
    }
    else if (typeName == nsSchemaAtoms::sDuration_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_DURATION;
    }
    else if (typeName == nsSchemaAtoms::sDate_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_DATE;
    }
    else if (typeName == nsSchemaAtoms::sGMonth_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_GMONTH;
    }
    else if (typeName == nsSchemaAtoms::sGYear_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_GYEAR;
    }
    else if (typeName == nsSchemaAtoms::sGYearMonth_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_GYEARMONTH;
    }
    else if (typeName == nsSchemaAtoms::sGDay_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_GDAY;
    }
    else if (typeName == nsSchemaAtoms::sGMonthDay_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_GMONTHDAY;
    }
    else if (typeName == nsSchemaAtoms::sName_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_NAME;
    }
    else if (typeName == nsSchemaAtoms::sQName_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_QNAME;
    }
    else if (typeName == nsSchemaAtoms::sNCName_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_NCNAME;
    }
    else if (typeName == nsSchemaAtoms::sAnyUri_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_ANYURI;
    }
    else if (typeName == nsSchemaAtoms::sLanguage_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_LANGUAGE;
    }
    else if (typeName == nsSchemaAtoms::sID_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_ID;
    }
    else if (typeName == nsSchemaAtoms::sIDREF_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_IDREF;
    }
    else if (typeName == nsSchemaAtoms::sIDREFS_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_IDREFS;
    }
    else if (typeName == nsSchemaAtoms::sENTITY_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_ENTITY;
    }
    else if (typeName == nsSchemaAtoms::sENTITIES_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_ENTITIES;
    }
    else if (typeName == nsSchemaAtoms::sNOTATION_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_NOTATION;
    }
    else if (typeName == nsSchemaAtoms::sNMTOKEN_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_NMTOKEN;
    }
    else if (typeName == nsSchemaAtoms::sNMTOKENS_atom) {
      typeVal = nsISchemaBuiltinType::BUILTIN_TYPE_NMTOKENS;
    }
    else {
      NS_ERROR("Unknown builtin type");
      return NS_ERROR_SCHEMA_UNKNOWN_TYPE;
    }

    nsSchemaBuiltinType* builtin = new nsSchemaBuiltinType(typeVal);
    if (!builtin) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    sup = builtin;
    mBuiltinTypesHash.Put(&key, sup);
    
    *aType = builtin;
    NS_ADDREF(*aType);
  }

  return NS_OK;
}

nsresult
nsBuiltinSchemaCollection::GetSOAPType(const nsAReadableString& aName,
                                       const nsAReadableString& aNamespace,
                                       nsISchemaType** aType)
{
  nsresult rv = NS_OK;
  nsStringKey key(aName);
  nsCOMPtr<nsISupports> sup = dont_AddRef(mSOAPTypeHash.Get(&key));
  if (sup) {
    rv = CallQueryInterface(sup, aType);
  }
  else {
    if (aName.Equals(NS_LITERAL_STRING("Array"))) {
      nsCOMPtr<nsISchemaType> anyType;
      rv = GetBuiltinType(NS_LITERAL_STRING("anyType"),
                          NS_LITERAL_STRING(NS_SCHEMA_2001_NAMESPACE),
                          getter_AddRefs(anyType));
      if (NS_FAILED(rv)) {
        return rv;
      }

      nsSOAPArray* array = new nsSOAPArray(anyType);
      if (!array) {
        return NS_ERROR_OUT_OF_MEMORY;
      }
      sup = array;
      mSOAPTypeHash.Put(&key, sup);
    
      *aType = array;
      NS_ADDREF(*aType);
    }
    else if (aName.Equals(NS_LITERAL_STRING("arrayType"))) {
      nsSOAPArrayType* arrayType = new nsSOAPArrayType();
      if (!arrayType) {
        return NS_ERROR_OUT_OF_MEMORY;
      }
      sup = arrayType;
      mSOAPTypeHash.Put(&key, sup);
    
      *aType = arrayType;
      NS_ADDREF(*aType);
    }
    else {
      rv = NS_ERROR_SCHEMA_UNKNOWN_TYPE;
    }
  }

  return rv;
}

////////////////////////////////////////////////////////////
//
// nsSchemaLoader implementation
//
////////////////////////////////////////////////////////////

nsSchemaLoader::nsSchemaLoader()
{
  NS_INIT_ISUPPORTS();
  mBuiltinCollection = do_GetService(NS_BUILTINSCHEMACOLLECTION_CONTRACTID);
}

nsSchemaLoader::~nsSchemaLoader()
{
}

NS_IMPL_ISUPPORTS2_CI(nsSchemaLoader, 
                      nsISchemaLoader, 
                      nsISchemaCollection)


/* nsISchema getSchema (in AString targetNamespace); */
NS_IMETHODIMP 
nsSchemaLoader::GetSchema(const nsAReadableString & targetNamespace, 
                          nsISchema **_retval)
{
  NS_ENSURE_ARG_POINTER(_retval);

  nsStringKey key(targetNamespace);
  nsCOMPtr<nsISupports> sup = dont_AddRef(mSchemas.Get(&key));
  nsCOMPtr<nsISchema> schema(do_QueryInterface(sup));

  if (!schema) {
    return NS_ERROR_SCHEMA_UNKNOWN_TARGET_NAMESPACE;  // no schema for specified targetname
  }

  *_retval = schema;
  NS_ADDREF(*_retval);

  return NS_OK;
}

/* nsISchemaElement getElement (in AString name, in AString namespace); */
NS_IMETHODIMP 
nsSchemaLoader::GetElement(const nsAReadableString & aName, 
                           const nsAReadableString & aNamespace, 
                           nsISchemaElement **_retval)
{
  nsCOMPtr<nsISchema> schema;
  nsresult rv = GetSchema(aNamespace, getter_AddRefs(schema));
  if (NS_FAILED(rv)) {
    return rv;
  }

  return schema->GetElementByName(aName, _retval);
}

/* nsISchemaAttribute getAttribute (in AString name, in AString namespace); */
NS_IMETHODIMP 
nsSchemaLoader::GetAttribute(const nsAReadableString & aName, 
                             const nsAReadableString & aNamespace, 
                             nsISchemaAttribute **_retval)
{
  nsCOMPtr<nsISchema> schema;
  nsresult rv = GetSchema(aNamespace, getter_AddRefs(schema));
  if (NS_FAILED(rv)) {
    return rv;
  }

  return schema->GetAttributeByName(aName, _retval);
}

/* nsISchemaType getType (in AString name, in AString namespace); */
NS_IMETHODIMP 
nsSchemaLoader::GetType(const nsAReadableString & aName, 
                        const nsAReadableString & aNamespace, 
                        nsISchemaType **_retval)
{
  if (IsSchemaNamespace(aNamespace) || IsSOAPNamespace(aNamespace)) {
    return mBuiltinCollection->GetType(aName, aNamespace, _retval);
  }

  nsCOMPtr<nsISchema> schema;
  nsresult rv = GetSchema(aNamespace, getter_AddRefs(schema));
  if (NS_FAILED(rv)) {
    return rv;
  }

  return schema->GetTypeByName(aName, _retval);
}

nsresult
nsSchemaLoader::GetResolvedURI(const nsAReadableString& aSchemaURI,
                               const char* aMethod,
                               nsIURI** aURI)
{
  nsresult rv;
  nsCOMPtr<nsIXPCNativeCallContext> cc;
  nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
  if(NS_SUCCEEDED(rv)) {
    rv = xpc->GetCurrentNativeCallContext(getter_AddRefs(cc));
  }

  if (NS_SUCCEEDED(rv) && cc) {
    JSContext* cx;
    rv = cc->GetJSContext(&cx);
    if (NS_FAILED(rv)) return rv;

    nsCOMPtr<nsIScriptSecurityManager> secMan(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
    if (NS_FAILED(rv)) return rv;

    nsCOMPtr<nsIURI> baseURI;
    nsCOMPtr<nsIPrincipal> principal;
    rv = secMan->GetSubjectPrincipal(getter_AddRefs(principal));
    if (NS_SUCCEEDED(rv)) {
      nsCOMPtr<nsICodebasePrincipal> codebase = do_QueryInterface(principal);
      if (codebase) {
        codebase->GetURI(getter_AddRefs(baseURI));
      }
    }
    
    rv = NS_NewURI(aURI, aSchemaURI, nsnull, baseURI);
    if (NS_FAILED(rv)) return rv;
    
    rv = secMan->CheckLoadURIFromScript(cx, *aURI);
    if (NS_FAILED(rv))
    {
      // Security check failed. The above call set a JS exception. The
      // following lines ensure that the exception is propagated.
      cc->SetExceptionWasThrown(PR_TRUE);
      return rv;
    }
  }
  else {
    rv = NS_NewURI(aURI, aSchemaURI, nsnull);
    if (NS_FAILED(rv)) return rv;
  }

  return NS_OK;
}

/* nsISchema load (in AString schemaURI); */
NS_IMETHODIMP 
nsSchemaLoader::Load(const nsAReadableString& schemaURI, 
                     nsISchema **_retval)
{
  NS_ENSURE_ARG_POINTER(_retval);

  nsCOMPtr<nsIURI> resolvedURI;
  nsresult rv = GetResolvedURI(schemaURI, "load", getter_AddRefs(resolvedURI));
  if (NS_FAILED(rv)) {
    return rv;
  }
  nsCAutoString spec;
  resolvedURI->GetSpec(spec);
  
  nsCOMPtr<nsIXMLHttpRequest> request(do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID, &rv));
  if (!request) {
    return rv;
  }

  rv = request->OpenRequest("GET",
                            spec.get(),
                            PR_FALSE, nsnull, nsnull);
  if (NS_FAILED(rv)) {
    return rv;
  }

  // Force the mimetype of the returned stream to be xml.
  rv = request->OverrideMimeType("text/xml");
  if (NS_FAILED(rv)) {
    return rv;
  }

  rv = request->Send(nsnull);
  if (NS_FAILED(rv)) {
    return rv;
  }

  nsCOMPtr<nsIDOMDocument> document;
  rv = request->GetResponseXML(getter_AddRefs(document));
  if (NS_FAILED(rv)) {
    return rv;
  }

  nsCOMPtr<nsIDOMElement> element;
  document->GetDocumentElement(getter_AddRefs(element));
  if (element) {
    rv = ProcessSchemaElement(element, _retval);
  }
  else {
    rv = NS_ERROR_SCHEMA_NOT_SCHEMA_ELEMENT;
  }
  
  return rv;
}

/* void loadAsync (in AString schemaURI, in nsISchemaLoadListener listener); */
NS_IMETHODIMP 
nsSchemaLoader::LoadAsync(const nsAReadableString& schemaURI, 
                          nsISchemaLoadListener *aListener)
{
  NS_ENSURE_ARG(aListener);

  nsCOMPtr<nsIURI> resolvedURI;
  nsresult rv = GetResolvedURI(schemaURI, "loadAsync", getter_AddRefs(resolvedURI));
  if (NS_FAILED(rv)) {
    return rv;
  }
  nsCAutoString spec;
  resolvedURI->GetSpec(spec);

  nsCOMPtr<nsIXMLHttpRequest> request(do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID, &rv));
  if (!request) {
    return rv;
  }

  rv = request->OpenRequest("GET",
                            spec.get(),
                            PR_TRUE, nsnull, nsnull);
  if (NS_FAILED(rv)) {
    return rv;
  }

  // Force the mimetype of the returned stream to be xml.
  rv = request->OverrideMimeType("text/xml");
  if (NS_FAILED(rv)) {
    return rv;
  }

  nsCOMPtr<nsIDOMEventListener> listener;
  LoadListener* listenerInst = new LoadListener(this, aListener, request);
  if (!listenerInst) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  listener = listenerInst;

  nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(request));
  if (!target) {
    return NS_ERROR_UNEXPECTED;
  }
  
  rv = target->AddEventListener(NS_LITERAL_STRING("load"),
                                listener, PR_FALSE);
  if (NS_FAILED(rv)) {
    return rv;
  }

  rv = target->AddEventListener(NS_LITERAL_STRING("error"),
                                listener, PR_FALSE);
  if (NS_FAILED(rv)) {
    return rv;
  }

  // The listener keeps the request alive until its complete
  rv = request->Send(nsnull);
  
  return rv;
}

static const char* kSchemaNamespaces[] = {NS_SCHEMA_1999_NAMESPACE, 
                                          NS_SCHEMA_2001_NAMESPACE};
static PRUint32 kSchemaNamespacesLength = sizeof(kSchemaNamespaces) / sizeof(const char*);

/* nsISchema processSchemaElement (in nsIDOMElement element); */
NS_IMETHODIMP 
nsSchemaLoader::ProcessSchemaElement(nsIDOMElement *element, 
                                     nsISchema **_retval)
{
  NS_ENSURE_ARG(element);
  NS_ENSURE_ARG_POINTER(_retval);

  nsresult rv = NS_OK;

  // Get target namespace and create the schema instance
  nsAutoString targetNamespace, schemaNamespace;
  element->GetAttribute(NS_LITERAL_STRING("targetNamespace"), 
                        targetNamespace);
  element->GetNamespaceURI(schemaNamespace);
  nsSchema* schemaInst = new nsSchema(this, targetNamespace, schemaNamespace);
  nsCOMPtr<nsISchema> schema = schemaInst;
  if (!schema) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  nsChildElementIterator iterator(element, 
                                  kSchemaNamespaces, kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;

  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {
    if (tagName == nsSchemaAtoms::sElement_atom) {
      nsCOMPtr<nsISchemaElement> schemaElement;
      rv = ProcessElement(schemaInst, childElement,  
                          getter_AddRefs(schemaElement));
      if (NS_SUCCEEDED(rv)) {
        rv = schemaInst->AddElement(schemaElement);
      }
    }
    else if (tagName == nsSchemaAtoms::sComplexType_atom) {
      nsCOMPtr<nsISchemaComplexType> complexType;
      rv = ProcessComplexType(schemaInst, childElement,
                              getter_AddRefs(complexType));
      if (NS_SUCCEEDED(rv)) {
        rv = schemaInst->AddType(complexType);
      }
    }
    else if (tagName == nsSchemaAtoms::sSimpleType_atom) {
      nsCOMPtr<nsISchemaSimpleType> simpleType;
      rv = ProcessSimpleType(schemaInst, childElement,
                             getter_AddRefs(simpleType));
      if (NS_SUCCEEDED(rv)) {
        rv = schemaInst->AddType(simpleType);
      }
    }
    else if (tagName == nsSchemaAtoms::sAttribute_atom) {
      nsCOMPtr<nsISchemaAttribute> attribute;
      rv = ProcessAttribute(schemaInst, childElement,
                            getter_AddRefs(attribute));
      if (NS_SUCCEEDED(rv)) {
        rv = schemaInst->AddAttribute(attribute);
      }
    }
    else if (tagName == nsSchemaAtoms::sAttributeGroup_atom) {
      nsCOMPtr<nsISchemaAttributeGroup> attributeGroup;
      rv = ProcessAttributeGroup(schemaInst, childElement,
                                 getter_AddRefs(attributeGroup));
      if (NS_SUCCEEDED(rv)) {
        rv = schemaInst->AddAttributeGroup(attributeGroup);
      }
    }
    else if (tagName == nsSchemaAtoms::sModelGroup_atom) {
      nsCOMPtr<nsISchemaModelGroup> modelGroup;
      rv = ProcessModelGroup(schemaInst, childElement,
                             tagName, nsnull, getter_AddRefs(modelGroup));
      if (NS_SUCCEEDED(rv)) {
        rv = schemaInst->AddModelGroup(modelGroup);
      }
    }
    // For now, ignore the following
    // annotations
    // include
    // import
    // redefine
    // notation
    // identity-constraint elements
    if (NS_FAILED(rv)) {
      return rv;
    }
  }

  // Resolve all forward references 
  rv = schemaInst->Resolve();
  if (NS_FAILED(rv)) {
    return rv;
  }

  nsStringKey key(targetNamespace);
  mSchemas.Put(&key, schema);

  *_retval = schema;
  NS_ADDREF(*_retval);

  return NS_OK;
}

PRBool 
ParseQualifiedName(nsIDOMElement* aContext,
                   const nsAReadableString& aQualifiedName,
                   nsAWritableString& aPrefix,
                   nsAWritableString& aLocalName,
                   nsAWritableString& aNamespaceURI) 
{
  nsReadingIterator<PRUnichar> pos, begin, end;
  
  aQualifiedName.BeginReading(begin);
  aQualifiedName.EndReading(end); 
  pos = begin;
  
  if (FindCharInReadable(PRUnichar(':'), pos, end)) {
    CopyUnicodeTo(begin, pos, aPrefix);
    CopyUnicodeTo(++pos, end, aLocalName);
  }
  else {
    CopyUnicodeTo(begin, end, aLocalName);
  }
  
  nsCOMPtr<nsIDOM3Node> node(do_QueryInterface(aContext));
  
  return node->LookupNamespaceURI(aPrefix, aNamespaceURI);
}

nsresult
nsSchemaLoader::GetNewOrUsedType(nsSchema* aSchema,
                                 nsIDOMElement* aContext,
                                 const nsAReadableString& aTypeName,
                                 nsISchemaType** aType)
{
  nsresult rv = NS_OK;
  nsAutoString prefix, localName, namespaceURI;

  // See if there's a prefix and get the
  // namespace associated with the prefix
  rv = ParseQualifiedName(aContext, aTypeName, prefix, 
                          localName, namespaceURI);
  if ((prefix.Length() > 0) && NS_FAILED(rv)) {
    // Unknown prefix
    return NS_ERROR_SCHEMA_UNKNOWN_PREFIX;
  }

  *aType = nsnull;
  nsAutoString targetNamespace;
  aSchema->GetTargetNamespace(targetNamespace);
  if (namespaceURI.IsEmpty() || namespaceURI.Equals(targetNamespace)) {
    // It's a local type 
    rv = aSchema->GetTypeByName(localName, aType);
  }
  else {
    rv = GetType(localName, namespaceURI, aType);
    if (!*aType) {
      return NS_ERROR_SCHEMA_UNKNOWN_TARGET_NAMESPACE;
    }
  }

  // If we didn't get a type, we need to create a placeholder
  if (NS_SUCCEEDED(rv) && !*aType) {
    nsSchemaTypePlaceholder* placeholder = new nsSchemaTypePlaceholder(aSchema,
                                                                       localName);
    if (!placeholder) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    *aType = placeholder;
    NS_ADDREF(*aType);
  }

  return rv;
}

nsresult 
nsSchemaLoader::ProcessElement(nsSchema* aSchema, 
                               nsIDOMElement* aElement,
                               nsISchemaElement** aSchemaElement)
{
  nsresult rv = NS_OK;

  nsCOMPtr<nsISchemaElement> schemaElement;
  PRUint32 minOccurs, maxOccurs;
  GetMinAndMax(aElement, &minOccurs, &maxOccurs);

  // See if it's a reference or an actual element declaration
  nsAutoString ref;
  aElement->GetAttribute(NS_LITERAL_STRING("ref"), ref);
  if (ref.Length() > 0) {
    nsSchemaElementRef* elementRef;
    
    elementRef = new nsSchemaElementRef(aSchema, ref);
    if (!elementRef) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    schemaElement = elementRef;

    elementRef->SetMinOccurs(minOccurs);
    elementRef->SetMaxOccurs(maxOccurs);
  }
  else {
    nsAutoString name;
    nsSchemaElement* elementInst;

    aElement->GetAttribute(NS_LITERAL_STRING("name"), name);
    elementInst = new nsSchemaElement(aSchema, name);
    if (!elementInst) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    schemaElement = elementInst;

    elementInst->SetMinOccurs(minOccurs);
    elementInst->SetMaxOccurs(maxOccurs);

    nsAutoString defaultValue, fixedValue;
    aElement->GetAttribute(NS_LITERAL_STRING("default"), defaultValue);
    aElement->GetAttribute(NS_LITERAL_STRING("fixed"), fixedValue);
    elementInst->SetConstraints(defaultValue, fixedValue);

    nsAutoString nillable, abstract;
    aElement->GetAttribute(NS_LITERAL_STRING("nillable"), nillable);
    aElement->GetAttribute(NS_LITERAL_STRING("abstract"), abstract);
    elementInst->SetFlags(nillable.Equals(NS_LITERAL_STRING("true")),
                          abstract.Equals(NS_LITERAL_STRING("true")));

    nsCOMPtr<nsISchemaType> schemaType;
    nsAutoString typeStr;
    aElement->GetAttribute(NS_LITERAL_STRING("type"), typeStr);
    if (typeStr.Length() > 0) {
      rv = GetNewOrUsedType(aSchema, aElement, typeStr, 
                            getter_AddRefs(schemaType));
      if (NS_FAILED(rv)) {
        return rv;
      }
    }
    // Look for the type as a child of the element 
    else {
      nsChildElementIterator iterator(aElement, 
                                      kSchemaNamespaces, 
                                      kSchemaNamespacesLength);
      nsCOMPtr<nsIDOMElement> childElement;
      nsCOMPtr<nsIAtom> tagName;

      while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                                getter_AddRefs(tagName))) &&
             childElement) {
        if (tagName == nsSchemaAtoms::sSimpleType_atom) {
          nsCOMPtr<nsISchemaSimpleType> simpleType;
          
          rv = ProcessSimpleType(aSchema, childElement,
                                 getter_AddRefs(simpleType));
          if (NS_FAILED(rv)) {
            return rv;
          }
          schemaType = simpleType;
          break;
        }
        else if (tagName == nsSchemaAtoms::sComplexType_atom) {
          nsCOMPtr<nsISchemaComplexType> complexType;
          
          rv = ProcessComplexType(aSchema, childElement,
                                  getter_AddRefs(complexType));
          if (NS_FAILED(rv)) {
            return rv;
          }
          schemaType = complexType;
          break;
        }
      }
    }

    if (!schemaType) {
      nsAutoString ns;
      aElement->GetNamespaceURI(ns);
      rv = GetType(NS_LITERAL_STRING("anyType"),
                   ns,
                   getter_AddRefs(schemaType));
      if (NS_FAILED(rv)) {
        return rv;
      }
    }

    rv = elementInst->SetType(schemaType);
    if (NS_FAILED(rv)) {
      return rv;
    }
  }

  *aSchemaElement = schemaElement;
  NS_ADDREF(*aSchemaElement);

  return NS_OK;
}

nsresult 
nsSchemaLoader::ProcessComplexType(nsSchema* aSchema, 
                                   nsIDOMElement* aElement,
                                   nsISchemaComplexType** aComplexType)
{
  nsresult rv = NS_OK;
  nsCOMPtr<nsISchemaComplexType> complexType;

  nsAutoString abstract, name;
  aElement->GetAttribute(NS_LITERAL_STRING("abstract"), abstract);
  aElement->GetAttribute(NS_LITERAL_STRING("name"), name);

  nsSchemaComplexType* typeInst;
  typeInst = new nsSchemaComplexType(aSchema, name, 
                                     abstract.Equals(NS_LITERAL_STRING("true")));
  if (!typeInst) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  complexType = typeInst;

  nsChildElementIterator iterator(aElement, 
                                  kSchemaNamespaces, 
                                  kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;

  PRUint16 contentModel = nsISchemaComplexType::CONTENT_MODEL_EMPTY;
  PRUint16 derivation = nsISchemaComplexType::DERIVATION_SELF_CONTAINED;
  nsCOMPtr<nsISchemaType> baseType;
  nsCOMPtr<nsISchemaModelGroup> modelGroup;
  
  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {
    if (tagName == nsSchemaAtoms::sSimpleContent_atom) {
      contentModel = nsISchemaComplexType::CONTENT_MODEL_SIMPLE;
      
      rv = ProcessSimpleContent(aSchema, 
                                childElement, typeInst,
                                &derivation, getter_AddRefs(baseType));
      break;
    }
    else if (tagName == nsSchemaAtoms::sComplexContent_atom) {       
      rv = ProcessComplexContent(aSchema, 
                                 childElement, typeInst, 
                                 &contentModel, &derivation,
                                 getter_AddRefs(baseType));
      break;                                   
    }
    else if (tagName != nsSchemaAtoms::sAnnotation_atom) {
      rv = ProcessComplexTypeBody(aSchema, 
                                  aElement, typeInst, nsnull,
                                  &contentModel);
      break;
    }
  }

  if (NS_FAILED(rv)) {
    return rv;
  }

  nsAutoString mixed;
  aElement->GetAttribute(NS_LITERAL_STRING("mixed"), mixed);
  if (mixed.Equals(NS_LITERAL_STRING("true"))) {
    contentModel = nsISchemaComplexType::CONTENT_MODEL_MIXED;
  }

  typeInst->SetContentModel(contentModel);
  typeInst->SetDerivation(derivation, baseType);

  *aComplexType = complexType;
  NS_ADDREF(*aComplexType);

  return NS_OK;
}

void
nsSchemaLoader::ConstructArrayName(nsISchemaType* aType,
                                   nsAWritableString& aName)
{
  nsAutoString typeName;
  
  aType->GetName(typeName);
  aName.Assign(NS_LITERAL_STRING("ArrayOf") + typeName);
}

nsresult
nsSchemaLoader::ParseDimensions(nsSchema* aSchema,
                                nsIDOMElement* aAttrElement,
                                const nsAReadableString& aStr,
                                nsISchemaType* aBaseType,
                                nsISchemaType** aArrayType,
                                PRUint32* aDimension)
{
  nsReadingIterator<PRUnichar> iter, done_reading;
  aStr.BeginReading(iter);
  aStr.EndReading(done_reading);

  PRUint32 dimension = 1;
  PRUnichar uc = *iter++;
  if (uc != PRUnichar('[')) {
    return NS_ERROR_UNEXPECTED;
  }

  while (iter != done_reading) {
    uc = *iter++;
    if (uc == PRUnichar(',')) {
      dimension++;
    }
    else if (uc == PRUnichar(']')) {
      break;
    }
  }
  *aDimension = dimension;

  while ((iter != done_reading) && (*iter == PRUnichar(' '))) {
    ++iter;
  }

  // If there's still more to go, then create an array type
  // based on the base and continue to parse
  if ((iter != done_reading) && (*iter == PRUnichar('['))) {
    nsAutoString name;
    nsCOMPtr<nsISchemaType> myArrayType;
    PRUint32 myDimension;
    
    nsresult rv = ParseDimensions(aSchema, aAttrElement,
                                  nsDependentSubstring(iter, done_reading),
                                  aBaseType, getter_AddRefs(myArrayType), 
                                  &myDimension);
    if (NS_FAILED(rv)) {
      return rv;
    }

    ConstructArrayName(myArrayType, name);
    nsSchemaComplexType* typeInst = new nsSchemaComplexType(aSchema,
                                                            name, 
                                                            PR_FALSE);
    if (!typeInst) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    nsCOMPtr<nsISchemaComplexType> complexType = typeInst;

    nsCOMPtr<nsISchemaType> soapArray;
    rv = GetType(NS_LITERAL_STRING("Array"),
                 NS_LITERAL_STRING(NS_SOAP_1_2_ENCODING_NAMESPACE),
                 getter_AddRefs(soapArray));
    if (NS_FAILED(rv)) {
      return rv;
    }

    typeInst->SetContentModel(nsISchemaComplexType::CONTENT_MODEL_ELEMENT_ONLY);
    typeInst->SetDerivation(nsISchemaComplexType::DERIVATION_RESTRICTION_COMPLEX,
                            soapArray);
    typeInst->SetArrayInfo(myArrayType, myDimension);

    *aArrayType = typeInst;
  }
  else {
    *aArrayType = aBaseType;
  }
  NS_ADDREF(*aArrayType);

  return NS_OK;
}

nsresult
nsSchemaLoader::ParseArrayType(nsSchema* aSchema,
                               nsIDOMElement* aAttrElement,
                               const nsAReadableString& aStr,
                               nsISchemaType** aType,
                               PRUint32* aDimension)
{
  PRInt32 offset;
  
  offset = aStr.FindChar(PRUnichar('['));
  if (offset == -1) {
    return NS_ERROR_SCHEMA_UNKNOWN_TYPE;
  }
  nsDependentSubstring typeStr(aStr, 0, offset);
          
  nsCOMPtr<nsISchemaType> type;
  nsresult rv = GetNewOrUsedType(aSchema, aAttrElement, typeStr, 
                                 getter_AddRefs(type));
  if (NS_FAILED(rv)) {
    return rv;
  }
  
  nsDependentSubstring dimensionStr(aStr, offset, 
                                    aStr.Length() - offset);
  return ParseDimensions(aSchema, aAttrElement, dimensionStr, type,
                         aType, aDimension);
}


nsresult
nsSchemaLoader::ProcessComplexTypeBody(nsSchema* aSchema, 
                                       nsIDOMElement* aElement,
                                       nsSchemaComplexType* aComplexType,
                                       nsSchemaModelGroup* aSequence,
                                       PRUint16* aContentModel)
{
  nsresult rv = NS_OK;
  nsChildElementIterator iterator(aElement, 
                                  kSchemaNamespaces, 
                                  kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;

  *aContentModel = nsISchemaComplexType::CONTENT_MODEL_EMPTY;

  nsCOMPtr<nsISchemaModelGroup> modelGroup;
  
  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {
    if ((tagName == nsSchemaAtoms::sModelGroup_atom) ||
        (tagName == nsSchemaAtoms::sAll_atom) ||
        (tagName == nsSchemaAtoms::sChoice_atom) || 
        (tagName == nsSchemaAtoms::sSequence_atom)) {
      // We shouldn't already have a model group
      if (modelGroup) {
        return NS_ERROR_SCHEMA_INVALID_STRUCTURE;
      }
      
      rv = ProcessModelGroup(aSchema, 
                             childElement, tagName,
                             aSequence, getter_AddRefs(modelGroup));
      if (NS_FAILED(rv)) {
        return rv;
      }

      PRUint32 particleCount;
      modelGroup->GetParticleCount(&particleCount);
      if (particleCount) {
        *aContentModel = nsISchemaComplexType::CONTENT_MODEL_ELEMENT_ONLY;
      }
      else {
        PRUint16 compositor;
        modelGroup->GetCompositor(&compositor);
        
        PRUint32 minOccurs;
        modelGroup->GetMinOccurs(&minOccurs);
        
        if ((compositor == nsISchemaModelGroup::COMPOSITOR_CHOICE) &&
            (minOccurs > 0)) {
          *aContentModel = nsISchemaComplexType::CONTENT_MODEL_ELEMENT_ONLY;
        }
      }
      
      if (aSequence) {
        // Check if we were collapsed
        if (modelGroup.get() != NS_STATIC_CAST(nsISchemaModelGroup*, 
                                               aSequence)) {
          rv = aSequence->AddParticle(modelGroup);
        }
      }
      else {
        rv = aComplexType->SetModelGroup(modelGroup);
      }
      if (NS_FAILED(rv)) {
        return rv;
      }        
    }
    else if ((tagName == nsSchemaAtoms::sAttribute_atom) ||
             (tagName == nsSchemaAtoms::sAttributeGroup_atom) ||
             (tagName == nsSchemaAtoms::sAnyAttribute_atom)) {
      nsCOMPtr<nsISchemaAttributeComponent> attribute;
      
      rv = ProcessAttributeComponent(aSchema, 
                                     childElement, tagName,
                                     getter_AddRefs(attribute));
      if (NS_FAILED(rv)) {
        return rv;
      }

      rv = aComplexType->AddAttribute(attribute);
      if (NS_FAILED(rv)) {
        return rv;
      }

      // XXX WSDL ugliness making itself into schemas. Hopefully this
      // mechanism for specifying an array type in schemas will die
      // when the Schema WG address qualified names in attribute
      // default values.
      if (tagName == nsSchemaAtoms::sAttribute_atom) {
#define NS_WSDL_NAMESPACE "http://schemas.xmlsoap.org/wsdl/"
        nsAutoString arrayType;
        childElement->GetAttributeNS(NS_LITERAL_STRING(NS_WSDL_NAMESPACE),
                                     NS_LITERAL_STRING("arrayType"), 
                                     arrayType);
        if (!arrayType.IsEmpty()) {
          nsCOMPtr<nsISchemaType> arraySchemaType;
          PRUint32 arrayDimension;
          rv = ParseArrayType(aSchema, 
                              childElement,
                              arrayType, 
                              getter_AddRefs(arraySchemaType),
                              &arrayDimension);
          if (NS_FAILED(rv)) {
            return rv;
          }

          rv = aComplexType->SetArrayInfo(arraySchemaType, arrayDimension);
          if (NS_FAILED(rv)) {
            return rv;
          }
        }
      }
    }
  }

  return rv;
}

nsresult 
nsSchemaLoader::ProcessSimpleContent(nsSchema* aSchema, 
                                     nsIDOMElement* aElement,
                                     nsSchemaComplexType* aComplexType,
                                     PRUint16* aDerivation,
                                     nsISchemaType** aBaseType)
{
  nsresult rv = NS_OK;

  nsCOMPtr<nsISchemaType> baseType;

  nsChildElementIterator iterator(aElement, 
                                  kSchemaNamespaces, 
                                  kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;
  
  // A simpleContent element must have children
  if (!iterator.HasChildNodes()) {
    return NS_ERROR_SCHEMA_INVALID_STRUCTURE;
  }

  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {       
    nsAutoString baseStr;
    if ((tagName == nsSchemaAtoms::sRestriction_atom) ||
        (tagName == nsSchemaAtoms::sExtension_atom)) {
      childElement->GetAttribute(NS_LITERAL_STRING("base"), baseStr);
      if (baseStr.Length() == 0) {
        return NS_ERROR_SCHEMA_MISSING_TYPE;
      }
      
      rv = GetNewOrUsedType(aSchema, childElement, baseStr, 
                            getter_AddRefs(baseType));
      if (NS_FAILED(rv)) {
        return rv;
      }

      nsCOMPtr<nsISchemaSimpleType> simpleBaseType;
      if (tagName == nsSchemaAtoms::sRestriction_atom) {
        *aDerivation = nsISchemaComplexType::DERIVATION_RESTRICTION_SIMPLE;
        rv = ProcessSimpleContentRestriction(aSchema, childElement,
                                             aComplexType, baseType,
                                             getter_AddRefs(simpleBaseType));
        if (NS_FAILED(rv)) {
          return rv;
        }
      }
      else {
        *aDerivation = nsISchemaComplexType::DERIVATION_EXTENSION_SIMPLE;
        
        nsCOMPtr<nsISchemaComplexType> complexBaseType(do_QueryInterface(baseType));
        if (complexBaseType) {
          // Copy over the attributes from the base type
          // XXX Should really be cloning
          PRUint32 attrIndex, attrCount;
          complexBaseType->GetAttributeCount(&attrCount);
          
          for (attrIndex = 0; attrIndex < attrCount; attrIndex++) {
            nsCOMPtr<nsISchemaAttributeComponent> attribute;
            
            rv = complexBaseType->GetAttributeByIndex(attrIndex,
                                                      getter_AddRefs(attribute));
            if (NS_FAILED(rv)) {
              return rv;
            }

            rv = aComplexType->AddAttribute(attribute);
            if (NS_FAILED(rv)) {
              return rv;
            }
          }
        }
        
        rv = ProcessSimpleContentExtension(aSchema, childElement,
                                           aComplexType, baseType,
                                           getter_AddRefs(simpleBaseType));
        if (NS_FAILED(rv)) {
          return rv;
        }
      }

      if (simpleBaseType) {
        rv = aComplexType->SetSimpleBaseType(simpleBaseType);
        if (NS_FAILED(rv)) {
          return rv;
        }
      }
      break;
    }
  }

  *aBaseType = baseType;
  NS_IF_ADDREF(*aBaseType);

  return NS_OK;
}

nsresult 
nsSchemaLoader::ProcessSimpleContentRestriction(nsSchema* aSchema, 
                                                nsIDOMElement* aElement,
                                                nsSchemaComplexType* aComplexType, 
                                                nsISchemaType* aBaseType,
                                                nsISchemaSimpleType** aSimpleBaseType)
{
  nsresult rv = NS_OK;

  nsChildElementIterator iterator(aElement, 
                                  kSchemaNamespaces, 
                                  kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;
  
  nsSchemaRestrictionType* restrictionInst;
  nsCOMPtr<nsISchemaSimpleType> simpleBase;
 
  restrictionInst = new nsSchemaRestrictionType(aSchema, 
                                                NS_LITERAL_STRING(""));
  if (!restrictionInst) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  simpleBase = restrictionInst;
  
  // The base type must actually be a complex type (which itself must
  // have a simple base type.
  nsCOMPtr<nsISchemaComplexType> complexBase = do_QueryInterface(aBaseType);
  if (!complexBase) {
    return NS_ERROR_SCHEMA_INVALID_TYPE_USAGE;
  }

  nsCOMPtr<nsISchemaSimpleType> parentSimpleBase;
  complexBase->GetSimpleBaseType(getter_AddRefs(parentSimpleBase));
  
  if (parentSimpleBase) {
    rv = restrictionInst->SetBaseType(parentSimpleBase);
    if (NS_FAILED(rv)) {
      return rv;
    }
  }

  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {       
    if (tagName == nsSchemaAtoms::sSimpleType_atom) {
      nsCOMPtr<nsISchemaSimpleType> simpleType;
      
      rv = ProcessSimpleType(aSchema, childElement, 
                             getter_AddRefs(simpleType));
      if (NS_FAILED(rv)) {
        return rv;
      }

      rv = restrictionInst->SetBaseType(simpleType);
      if (NS_FAILED(rv)) {
        return rv;
      }
    }
    else if ((tagName == nsSchemaAtoms::sMinExclusive_atom) ||
             (tagName == nsSchemaAtoms::sMinInclusive_atom) ||
             (tagName == nsSchemaAtoms::sMaxExclusive_atom) ||
             (tagName == nsSchemaAtoms::sMaxInclusive_atom) ||
             (tagName == nsSchemaAtoms::sTotalDigits_atom) ||
             (tagName == nsSchemaAtoms::sFractionDigits_atom) ||
             (tagName == nsSchemaAtoms::sLength_atom) ||
             (tagName == nsSchemaAtoms::sMinLength_atom) ||
             (tagName == nsSchemaAtoms::sMaxLength_atom) ||
             (tagName == nsSchemaAtoms::sEnumeration_atom) ||
             (tagName == nsSchemaAtoms::sWhiteSpace_atom) ||
             (tagName == nsSchemaAtoms::sPattern_atom)) {
      nsCOMPtr<nsISchemaFacet> facet;
      
      rv = ProcessFacet(aSchema, childElement, 
                        tagName, getter_AddRefs(facet));
      if (NS_FAILED(rv)) {
        return rv;
      }

      rv = restrictionInst->AddFacet(facet);
      if (NS_FAILED(rv)) {
        return rv;
      }
    }
    else if ((tagName == nsSchemaAtoms::sAttribute_atom) ||
             (tagName == nsSchemaAtoms::sAttributeGroup_atom) ||
             (tagName == nsSchemaAtoms::sAnyAttribute_atom)) {
      nsCOMPtr<nsISchemaAttributeComponent> attribute;
      
      rv = ProcessAttributeComponent(aSchema,
                                     childElement, tagName,
                                     getter_AddRefs(attribute));
      if (NS_FAILED(rv)) {
        return rv;
      }

      rv = aComplexType->AddAttribute(attribute);
      if (NS_FAILED(rv)) {
        return rv;
      }
    }
  }
  
  *aSimpleBaseType = simpleBase;
  NS_IF_ADDREF(*aSimpleBaseType);

  return NS_OK;
}
 
nsresult 
nsSchemaLoader::ProcessSimpleContentExtension(nsSchema* aSchema, 
                                              nsIDOMElement* aElement,
                                              nsSchemaComplexType* aComplexType,
                                              nsISchemaType* aBaseType,
                                              nsISchemaSimpleType** aSimpleBaseType)
{
  nsresult rv = NS_OK;

  nsChildElementIterator iterator(aElement, 
                                  kSchemaNamespaces, 
                                  kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;

  // If the base type is a complex type, it must itself have a simple
  // base type
  nsCOMPtr<nsISchemaComplexType> complexBase = do_QueryInterface(aBaseType);
  if (complexBase) {
    complexBase->GetSimpleBaseType(aSimpleBaseType);
  }
  else {
    aBaseType->QueryInterface(NS_GET_IID(nsISchemaSimpleType),
                              (void**)aSimpleBaseType);
  }

  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {       
    if ((tagName == nsSchemaAtoms::sAttribute_atom) ||
        (tagName == nsSchemaAtoms::sAttributeGroup_atom) ||
        (tagName == nsSchemaAtoms::sAnyAttribute_atom)) {
      nsCOMPtr<nsISchemaAttributeComponent> attribute;
      
      rv = ProcessAttributeComponent(aSchema, 
                                     childElement, tagName,
                                     getter_AddRefs(attribute));
      if (NS_FAILED(rv)) {
        return rv;
      }
      
      rv = aComplexType->AddAttribute(attribute);
      if (NS_FAILED(rv)) {
        return rv;
      }
    }
  }
  
  return NS_OK;
}
 
nsresult 
nsSchemaLoader::ProcessComplexContent(nsSchema* aSchema, 
                                      nsIDOMElement* aElement,
                                      nsSchemaComplexType* aComplexType,
                                      PRUint16* aContentModel,
                                      PRUint16* aDerivation,
                                      nsISchemaType** aBaseType)
{
  nsresult rv = NS_OK;

  nsCOMPtr<nsISchemaType> baseType;
  nsChildElementIterator iterator(aElement, 
                                  kSchemaNamespaces, 
                                  kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;
  
  // A complexContent element must have children
  if (!iterator.HasChildNodes()) {
    return NS_ERROR_SCHEMA_INVALID_STRUCTURE;
  }
  
  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {       
    nsAutoString baseStr;
    if ((tagName == nsSchemaAtoms::sRestriction_atom) ||
        (tagName == nsSchemaAtoms::sExtension_atom)) {
      childElement->GetAttribute(NS_LITERAL_STRING("base"), baseStr);
      if (baseStr.Length() == 0) {
        return NS_ERROR_SCHEMA_MISSING_TYPE;
      }
      
      rv = GetNewOrUsedType(aSchema, childElement, baseStr, 
                            getter_AddRefs(baseType));
      if (NS_FAILED(rv)) {
        return rv;
      }
      
      if (tagName == nsSchemaAtoms::sRestriction_atom) {
        *aDerivation = nsISchemaComplexType::DERIVATION_RESTRICTION_COMPLEX;
        rv = ProcessComplexTypeBody(aSchema, childElement,
                                    aComplexType, nsnull, aContentModel);
      }
      else {
        *aDerivation = nsISchemaComplexType::DERIVATION_EXTENSION_COMPLEX;
        
        nsCOMPtr<nsISchemaModelGroup> sequence;
        nsSchemaModelGroup* sequenceInst = nsnull;
        nsCOMPtr<nsISchemaComplexType> complexBaseType(do_QueryInterface(baseType));
        if (complexBaseType) {
          // XXX Should really be cloning
          nsCOMPtr<nsISchemaModelGroup> baseGroup;
          rv = complexBaseType->GetModelGroup(getter_AddRefs(baseGroup));
          if (NS_FAILED(rv)) {
            return rv;
          }
          
          if (baseGroup) {
            // Create a new model group that's going to be the a sequence
            // of the base model group and the content below
            sequenceInst = new nsSchemaModelGroup(aSchema,
                                                  NS_LITERAL_STRING(""));
            if (!sequenceInst) {
              return NS_ERROR_OUT_OF_MEMORY;
            }
            sequence = sequenceInst;

            PRUint16 compositor;
            baseGroup->GetCompositor(&compositor);

            PRUint32 minOccurs, maxOccurs;
            baseGroup->GetMinOccurs(&minOccurs);
            baseGroup->GetMaxOccurs(&maxOccurs);

            // If the base group also a sequence, we can collapse the 
            // two sequences.
            if ((compositor == nsISchemaModelGroup::COMPOSITOR_SEQUENCE) &&
                (minOccurs == 1) && (maxOccurs == 1)) {
              PRUint32 pIndex, pCount;
              baseGroup->GetParticleCount(&pCount);
              for (pIndex = 0; pIndex < pCount; pIndex++) {
                nsCOMPtr<nsISchemaParticle> particle;
                
                rv = baseGroup->GetParticle(pIndex, getter_AddRefs(particle));
                if (NS_FAILED(rv)) {
                  return rv;
                }
                
                rv = sequenceInst->AddParticle(particle);
                if (NS_FAILED(rv)) {
                  return rv;
                }
              }
            }
            else {
              sequenceInst->AddParticle(baseGroup);
            }
            
            aComplexType->SetModelGroup(sequence);
          }            
          
          
          // Copy over the attributes from the base type
          // XXX Should really be cloning
          PRUint32 attrIndex, attrCount;
          complexBaseType->GetAttributeCount(&attrCount);
          
          for (attrIndex = 0; attrIndex < attrCount; attrIndex++) {
            nsCOMPtr<nsISchemaAttributeComponent> attribute;
            
            rv = complexBaseType->GetAttributeByIndex(attrIndex,
                                                      getter_AddRefs(attribute));
            if (NS_FAILED(rv)) {
              return rv;
            }

            rv = aComplexType->AddAttribute(attribute);
            if (NS_FAILED(rv)) {
              return rv;
            }
          }
        }
        
        PRUint16 explicitContent;
        rv = ProcessComplexTypeBody(aSchema, childElement,
                                    aComplexType, sequenceInst,
                                    &explicitContent);
        if (NS_FAILED(rv)) {
          return rv;
        }
        // If the explicit content is empty, get the content type
        // from the base
        if ((explicitContent == nsISchemaComplexType::CONTENT_MODEL_EMPTY) &&
            complexBaseType) {
          complexBaseType->GetContentModel(aContentModel);
        }
        else {
          *aContentModel = explicitContent;
        }
      }
      break;
    }
  }

  nsAutoString mixed;
  aElement->GetAttribute(NS_LITERAL_STRING("mixed"), mixed);
  if (mixed.Equals(NS_LITERAL_STRING("true"))) {
    *aContentModel = nsISchemaComplexType::CONTENT_MODEL_MIXED;
  }

  *aBaseType = baseType;
  NS_IF_ADDREF(*aBaseType);

  return NS_OK;
}

nsresult 
nsSchemaLoader::ProcessSimpleType(nsSchema* aSchema, 
                                  nsIDOMElement* aElement,
                                  nsISchemaSimpleType** aSimpleType)
{
  nsresult rv = NS_OK;

  nsAutoString name;
  aElement->GetAttribute(NS_LITERAL_STRING("name"), name);

  nsChildElementIterator iterator(aElement, 
                                  kSchemaNamespaces, 
                                  kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;

  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {
    if (tagName == nsSchemaAtoms::sRestriction_atom) {
      rv = ProcessSimpleTypeRestriction(aSchema, childElement,
                                        name, aSimpleType);
      break;
    }
    else if (tagName == nsSchemaAtoms::sList_atom) {
      rv = ProcessSimpleTypeList(aSchema, childElement,
                                 name, aSimpleType);
      break;
    }
    else if (tagName = nsSchemaAtoms::sUnion_atom) {
      rv = ProcessSimpleTypeUnion(aSchema, childElement,
                                  name, aSimpleType);
      break;
    }
  }  
  
  return rv;
}

nsresult 
nsSchemaLoader::ProcessSimpleTypeRestriction(nsSchema* aSchema, 
                                             nsIDOMElement* aElement,
                                             const nsAReadableString& aName,
                                             nsISchemaSimpleType** aSimpleType)
{
  nsresult rv = NS_OK;

  nsSchemaRestrictionType* restrictionInst;
  nsCOMPtr<nsISchemaSimpleType> restriction;
 
  restrictionInst = new nsSchemaRestrictionType(aSchema, aName);
  if (!restrictionInst) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  restriction = restrictionInst;

  nsCOMPtr<nsISchemaType> baseType;
  nsAutoString baseStr;
  aElement->GetAttribute(NS_LITERAL_STRING("base"), baseStr);
  if (baseStr.Length() > 0) {
    rv = GetNewOrUsedType(aSchema, aElement, baseStr, 
                          getter_AddRefs(baseType));
    if (NS_FAILED(rv)) {
      return rv;
    }

    nsCOMPtr<nsISchemaSimpleType> simpleBase(do_QueryInterface(baseType));
    if (!simpleBase) {
      return NS_ERROR_SCHEMA_INVALID_TYPE_USAGE;
    }
    rv = restrictionInst->SetBaseType(simpleBase);
  }

  nsChildElementIterator iterator(aElement, 
                                  kSchemaNamespaces, 
                                  kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;

  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {
    if ((tagName == nsSchemaAtoms::sSimpleType_atom) &&
        !baseType) {
      nsCOMPtr<nsISchemaSimpleType> simpleType;
      
      rv = ProcessSimpleType(aSchema, childElement, 
                             getter_AddRefs(simpleType));
      if (NS_FAILED(rv)) {
        return rv;
      }

      rv = restrictionInst->SetBaseType(simpleType);
      if (NS_FAILED(rv)) {
        return rv;
      }
      baseType = simpleType;
    }
    else if ((tagName == nsSchemaAtoms::sMinExclusive_atom) ||
             (tagName == nsSchemaAtoms::sMinInclusive_atom) ||
             (tagName == nsSchemaAtoms::sMaxExclusive_atom) ||
             (tagName == nsSchemaAtoms::sMaxInclusive_atom) ||
             (tagName == nsSchemaAtoms::sTotalDigits_atom) ||
             (tagName == nsSchemaAtoms::sFractionDigits_atom) ||
             (tagName == nsSchemaAtoms::sLength_atom) ||
             (tagName == nsSchemaAtoms::sMinLength_atom) ||
             (tagName == nsSchemaAtoms::sMaxLength_atom) ||
             (tagName == nsSchemaAtoms::sEnumeration_atom) ||
             (tagName == nsSchemaAtoms::sWhiteSpace_atom) ||
             (tagName == nsSchemaAtoms::sPattern_atom)) {
      nsCOMPtr<nsISchemaFacet> facet;
      
      rv = ProcessFacet(aSchema, childElement, 
                        tagName, getter_AddRefs(facet));
      if (NS_FAILED(rv)) {
        return rv;
      }

      rv = restrictionInst->AddFacet(facet);
      if (NS_FAILED(rv)) {
        return rv;
      }
    }
  }

  *aSimpleType = restriction;
  NS_ADDREF(*aSimpleType);

  return NS_OK;
}
 
nsresult 
nsSchemaLoader::ProcessSimpleTypeList(nsSchema* aSchema, 
                                      nsIDOMElement* aElement,
                                      const nsAReadableString& aName,
                                      nsISchemaSimpleType** aSimpleType)
{
  nsresult rv = NS_OK;

  nsSchemaListType* listInst;
  nsCOMPtr<nsISchemaSimpleType> list;

  listInst = new nsSchemaListType(aSchema, aName);
  if (!listInst) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  list = listInst;

  nsAutoString itemTypeStr;
  aElement->GetAttribute(NS_LITERAL_STRING("itemType"), itemTypeStr);

  nsCOMPtr<nsISchemaSimpleType> itemType;
  if (itemTypeStr.Length() > 0) {
    nsCOMPtr<nsISchemaType> type;
    rv = GetNewOrUsedType(aSchema, aElement, itemTypeStr, 
                          getter_AddRefs(type));
    if (NS_FAILED(rv)) {
      return rv;
    }

    itemType = do_QueryInterface(type);
  }
  else {
    nsChildElementIterator iterator(aElement, 
                                    kSchemaNamespaces, 
                                    kSchemaNamespacesLength);
    nsCOMPtr<nsIDOMElement> childElement;
    nsCOMPtr<nsIAtom> tagName;
    
    while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                              getter_AddRefs(tagName))) &&
           childElement) {
      if (tagName == nsSchemaAtoms::sSimpleType_atom) {
        rv = ProcessSimpleType(aSchema, childElement,
                               getter_AddRefs(itemType));
        if (NS_FAILED(rv)) {
          return rv;
        }
        break;
      }
    }
  }

  if (!itemType) {
    return NS_ERROR_SCHEMA_MISSING_TYPE;
  }
  listInst->SetListType(itemType);

  *aSimpleType = list;
  NS_ADDREF(*aSimpleType);

  return NS_OK;
}

nsresult 
nsSchemaLoader::ProcessSimpleTypeUnion(nsSchema* aSchema, 
                                       nsIDOMElement* aElement,
                                       const nsAReadableString& aName,
                                       nsISchemaSimpleType** aSimpleType)
{
  nsresult rv = NS_OK;

  nsSchemaUnionType* unionInst;
  nsCOMPtr<nsISchemaSimpleType> unionType;

  unionInst = new nsSchemaUnionType(aSchema, aName);
  if (!unionInst) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  unionType = unionInst;

  nsCOMPtr<nsISchemaSimpleType> memberType;
  nsAutoString memberTypes;
  aElement->GetAttribute(NS_LITERAL_STRING("memberTypes"), memberTypes);
  if (memberTypes.Length() > 0) {
    nsReadingIterator<PRUnichar> begin, end, tokenEnd;

    memberTypes.BeginReading(tokenEnd);
    memberTypes.EndReading(end);

    while (tokenEnd != end) {
      nsAutoString typeStr;
      begin = tokenEnd;
      if (FindCharInReadable(PRUnichar(' '), tokenEnd, end)) {
        CopyUnicodeTo(begin, tokenEnd, typeStr);
        ++tokenEnd;
      }
      else {
        CopyUnicodeTo(begin, end, typeStr);
      }

      nsCOMPtr<nsISchemaType> type;
      rv = GetNewOrUsedType(aSchema, aElement, typeStr, 
                            getter_AddRefs(type));
      if (NS_FAILED(rv)) {
        return rv;
      }
      
      memberType = do_QueryInterface(type);
      if (!memberType) {
        return NS_ERROR_SCHEMA_INVALID_TYPE_USAGE;
      }

      rv = unionInst->AddUnionType(memberType);
      if (NS_FAILED(rv)) {
        return rv;
      }            
    }
  }
  
  nsChildElementIterator iterator(aElement, 
                                  kSchemaNamespaces, 
                                  kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;
  
  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {
    if (tagName == nsSchemaAtoms::sSimpleType_atom) {
      rv = ProcessSimpleType(aSchema, childElement,
                             getter_AddRefs(memberType));
      if (NS_FAILED(rv)) {
        return rv;
      }
      
      rv = unionInst->AddUnionType(memberType);
      if (NS_FAILED(rv)) {
        return rv;
      }      
    }
  }

  *aSimpleType = unionType;
  NS_ADDREF(*aSimpleType);

  return NS_OK;
}

nsresult 
nsSchemaLoader::ProcessModelGroup(nsSchema* aSchema, 
                                  nsIDOMElement* aElement,
                                  nsIAtom* aTagName,
                                  nsSchemaModelGroup* aParentSequence,
                                  nsISchemaModelGroup** aModelGroup)
{
  nsresult rv = NS_OK;

  nsCOMPtr<nsISchemaModelGroup> modelGroup;
  PRUint32 minOccurs, maxOccurs;
  GetMinAndMax(aElement, &minOccurs, &maxOccurs);

  // Check for a ref attribute
  nsAutoString ref;
  aElement->GetAttribute(NS_LITERAL_STRING("ref"), ref);
  
  if ((aTagName == nsSchemaAtoms::sModelGroup_atom) &&
      (ref.Length() > 0)) {
    nsSchemaModelGroupRef* modelGroupRef = new nsSchemaModelGroupRef(aSchema, 
                                                                     ref);
    if (!modelGroupRef) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    modelGroup = modelGroupRef;
    
    modelGroupRef->SetMinOccurs(minOccurs);
    modelGroupRef->SetMaxOccurs(maxOccurs);
  }
  else {
    nsAutoString name;
    aElement->GetAttribute(NS_LITERAL_STRING("name"), name);

    nsChildElementIterator iterator(aElement, 
                                    kSchemaNamespaces, 
                                    kSchemaNamespacesLength);
    nsCOMPtr<nsIDOMElement> childElement;
    nsCOMPtr<nsIAtom> tagName = aTagName;

    // If this is a group element, find the first compositor
    // child and continue with that.
    if (aTagName == nsSchemaAtoms::sModelGroup_atom) {
      while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                                getter_AddRefs(tagName))) &&
             childElement) {
        if ((tagName == nsSchemaAtoms::sAll_atom) ||
            (tagName == nsSchemaAtoms::sChoice_atom) ||
            (tagName == nsSchemaAtoms::sSequence_atom)) {
          iterator.SetElement(childElement);
          break;
        }
      }
    }

    nsSchemaModelGroup* modelGroupInst;

    // If we have a parent sequence and we're a sequence that
    // only appears once, then collapse us.
    if (aParentSequence && 
        (tagName == nsSchemaAtoms::sSequence_atom) &&
        (minOccurs == 1) && (maxOccurs == 1)) {
      modelGroupInst = aParentSequence;
      modelGroup = modelGroupInst;
    }
    else {
      modelGroupInst = new nsSchemaModelGroup(aSchema, name);
      if (!modelGroupInst) {
        return NS_ERROR_OUT_OF_MEMORY;
      }
      modelGroup = modelGroupInst;
      
      modelGroupInst->SetMinOccurs(minOccurs);
      modelGroupInst->SetMaxOccurs(maxOccurs);


      if (tagName == nsSchemaAtoms::sAll_atom) {
        modelGroupInst->SetCompositor(nsISchemaModelGroup::COMPOSITOR_ALL);
      }
      else if (tagName == nsSchemaAtoms::sChoice_atom) {
        modelGroupInst->SetCompositor(nsISchemaModelGroup::COMPOSITOR_CHOICE);
      }
      else if (tagName == nsSchemaAtoms::sSequence_atom) {
        modelGroupInst->SetCompositor(nsISchemaModelGroup::COMPOSITOR_SEQUENCE);
      }
    }
    
    while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                              getter_AddRefs(tagName))) &&
           childElement) {
      if (tagName != nsSchemaAtoms::sAnnotation_atom) {
        nsCOMPtr<nsISchemaParticle> particle;

        rv = ProcessParticle(aSchema, childElement,
                             tagName, getter_AddRefs(particle));
        if (NS_FAILED(rv)) {
          return rv;
        }

        rv = modelGroupInst->AddParticle(particle);
        if (NS_FAILED(rv)) {
          return rv;
        }
      }
    }   
  }

  *aModelGroup = modelGroup;
  NS_ADDREF(*aModelGroup);

  return NS_OK;
}

nsresult 
nsSchemaLoader::ProcessParticle(nsSchema* aSchema, 
                                nsIDOMElement* aElement,
                                nsIAtom* aTagName,
                                nsISchemaParticle** aParticle)
{
  nsresult rv;

  if (aTagName == nsSchemaAtoms::sElement_atom) {
    nsCOMPtr<nsISchemaElement> element;

    rv = ProcessElement(aSchema, aElement, getter_AddRefs(element));
    if (NS_FAILED(rv)) {
      return rv;
    }
    *aParticle = element;
    NS_IF_ADDREF(*aParticle);
  }
  else if ((aTagName == nsSchemaAtoms::sModelGroup_atom) ||
           (aTagName == nsSchemaAtoms::sChoice_atom) ||
           (aTagName == nsSchemaAtoms::sSequence_atom)) {
    nsCOMPtr<nsISchemaModelGroup> modelGroup;
    
    rv = ProcessModelGroup(aSchema, aElement, 
                           aTagName, nsnull, getter_AddRefs(modelGroup));
    if (NS_FAILED(rv)) {
      return rv;
    }
    *aParticle = modelGroup;
    NS_IF_ADDREF(*aParticle);
  }
  else if (aTagName == nsSchemaAtoms::sAny_atom) {

    nsCOMPtr<nsISchemaParticle> anyParticle;
    nsSchemaAnyParticle* anyParticleInst = new nsSchemaAnyParticle(aSchema);
    if (!anyParticleInst) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    anyParticle = anyParticleInst;

    PRUint32 minOccurs, maxOccurs;
    GetMinAndMax(aElement, &minOccurs, &maxOccurs);
    anyParticleInst->SetMinOccurs(minOccurs);
    anyParticleInst->SetMaxOccurs(maxOccurs);

    PRUint16 process;
    GetProcess(aElement, &process);
    anyParticleInst->SetProcess(process);

    nsAutoString namespaceStr;
    aElement->GetAttribute(NS_LITERAL_STRING("namespace"), namespaceStr);
    anyParticleInst->SetNamespace(namespaceStr);

    *aParticle = anyParticle;
    NS_ADDREF(*aParticle);
  
  }

  return NS_OK;
}

nsresult 
nsSchemaLoader::ProcessAttributeComponent(nsSchema* aSchema, 
                                          nsIDOMElement* aElement,
                                          nsIAtom* aTagName,
                                          nsISchemaAttributeComponent** aAttribute)
{
  nsresult rv;

  if (aTagName == nsSchemaAtoms::sAttribute_atom) {
    nsCOMPtr<nsISchemaAttribute> attribute;

    rv = ProcessAttribute(aSchema, aElement, 
                          getter_AddRefs(attribute));
    if (NS_FAILED(rv)) {
      return rv;
    }
    *aAttribute = attribute;
    NS_IF_ADDREF(*aAttribute);
  }
  else if (aTagName == nsSchemaAtoms::sAttributeGroup_atom) {
    nsCOMPtr<nsISchemaAttributeGroup> attributeGroup;

    rv = ProcessAttributeGroup(aSchema, aElement, 
                               getter_AddRefs(attributeGroup));
    if (NS_FAILED(rv)) {
      return rv;
    }
    *aAttribute = attributeGroup;
    NS_IF_ADDREF(*aAttribute);
  }
  else if (aTagName == nsSchemaAtoms::sAnyAttribute_atom) {
    nsCOMPtr<nsISchemaAttributeComponent> anyAttribute;
    nsSchemaAnyAttribute* anyAttributeInst = new nsSchemaAnyAttribute(aSchema);
    if (!anyAttributeInst) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    anyAttribute = anyAttributeInst;

    PRUint16 process;
    GetProcess(aElement, &process);
    anyAttributeInst->SetProcess(process);

    nsAutoString namespaceStr;
    aElement->GetAttribute(NS_LITERAL_STRING("namespace"), namespaceStr);
    anyAttributeInst->SetNamespace(namespaceStr);
    
    *aAttribute = anyAttribute;
    NS_ADDREF(*aAttribute);

  }

  return NS_OK;
}

nsresult 
nsSchemaLoader::ProcessAttribute(nsSchema* aSchema, 
                                 nsIDOMElement* aElement,
                                 nsISchemaAttribute** aAttribute)
{
  nsresult rv;

  nsCOMPtr<nsISchemaAttribute> attribute;

  nsAutoString defaultValue, fixedValue;
  aElement->GetAttribute(NS_LITERAL_STRING("default"), defaultValue);
  aElement->GetAttribute(NS_LITERAL_STRING("fixed"), fixedValue);

  PRUint16 use;
  GetUse(aElement, &use);

  nsAutoString ref;
  aElement->GetAttribute(NS_LITERAL_STRING("ref"), ref);
  if (ref.Length() > 0) {
    nsSchemaAttributeRef* attributeRef = new nsSchemaAttributeRef(aSchema,
                                                                  ref);
    if (!attributeRef) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    attribute = attributeRef;

    attributeRef->SetConstraints(defaultValue, fixedValue);
    attributeRef->SetUse(use);
  }
  else {
    nsAutoString name;
    aElement->GetAttribute(NS_LITERAL_STRING("name"), name);
    
    nsSchemaAttribute* attributeInst = new nsSchemaAttribute(aSchema,
                                                             name);
    if (!attributeInst) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    attribute = attributeInst;

    attributeInst->SetConstraints(defaultValue, fixedValue);
    attributeInst->SetUse(use);

    nsCOMPtr<nsISchemaSimpleType> simpleType;

    nsChildElementIterator iterator(aElement, 
                                    kSchemaNamespaces, 
                                    kSchemaNamespacesLength);
    nsCOMPtr<nsIDOMElement> childElement;
    nsCOMPtr<nsIAtom> tagName;

    while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                              getter_AddRefs(tagName))) &&
           childElement) {
      if (tagName == nsSchemaAtoms::sSimpleType_atom) {
        rv = ProcessSimpleType(aSchema, childElement,
                               getter_AddRefs(simpleType));
        if (NS_FAILED(rv)) {
          return rv;
        }
        break;
      }
    }

    if (!simpleType) {
      nsAutoString typeStr;
      aElement->GetAttribute(NS_LITERAL_STRING("type"), typeStr);

      if (typeStr.Length() > 0) {
        nsCOMPtr<nsISchemaType> schemaType;
        rv = GetNewOrUsedType(aSchema, aElement, typeStr, 
                              getter_AddRefs(schemaType));
        if (NS_FAILED(rv)) {
          return rv;
        }

        simpleType = do_QueryInterface(schemaType);
        if (!simpleType) {
          return NS_ERROR_SCHEMA_INVALID_TYPE_USAGE;
        }
      }
    }

    attributeInst->SetType(simpleType);
  }

  *aAttribute = attribute;
  NS_ADDREF(*aAttribute);

  return NS_OK;
}

nsresult 
nsSchemaLoader::ProcessAttributeGroup(nsSchema* aSchema, 
                                      nsIDOMElement* aElement,
                                      nsISchemaAttributeGroup** aAttributeGroup)
{
  nsresult rv;

  nsCOMPtr<nsISchemaAttributeGroup> attributeGroup;

  nsAutoString ref;
  aElement->GetAttribute(NS_LITERAL_STRING("ref"), ref);

  if (ref.Length() > 0) {
    nsSchemaAttributeGroupRef* attrRef = new nsSchemaAttributeGroupRef(aSchema,
                                                                       ref);
    if (!attrRef) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    attributeGroup = attrRef;
  }
  else {
    nsAutoString name;
    aElement->GetAttribute(NS_LITERAL_STRING("name"), name);
    
    nsSchemaAttributeGroup* attrInst = new nsSchemaAttributeGroup(aSchema,
                                                                  name);
    if (!attrInst) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    attributeGroup = attrInst;

    nsChildElementIterator iterator(aElement, 
                                    kSchemaNamespaces, 
                                    kSchemaNamespacesLength);
    nsCOMPtr<nsIDOMElement> childElement;
    nsCOMPtr<nsIAtom> tagName;

    while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                              getter_AddRefs(tagName))) &&
           childElement) {
      if ((tagName == nsSchemaAtoms::sAttribute_atom) ||
          (tagName == nsSchemaAtoms::sAttributeGroup_atom) ||
          (tagName == nsSchemaAtoms::sAnyAttribute_atom)) {
        nsCOMPtr<nsISchemaAttributeComponent> attribute;
        
        rv = ProcessAttributeComponent(aSchema, 
                                       childElement, tagName,
                                       getter_AddRefs(attribute));
        if (NS_FAILED(rv)) {
          return rv;
        }
        
        rv = attrInst->AddAttribute(attribute);
        if (NS_FAILED(rv)) {
          return rv;
        }
      }    
    }
  }

  *aAttributeGroup = attributeGroup;
  NS_ADDREF(*aAttributeGroup);

  return NS_OK;
}
 
nsresult 
nsSchemaLoader::ProcessFacet(nsSchema* aSchema, 
                             nsIDOMElement* aElement,
                             nsIAtom* aTagName,
                             nsISchemaFacet** aFacet)
{
  PRInt32 rv;

  nsCOMPtr<nsISchemaFacet> facet;
  nsSchemaFacet* facetInst = new nsSchemaFacet(aSchema);
  if (!facetInst) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  facet = facetInst;

  PRUint16 facetType;
  if (aTagName == nsSchemaAtoms::sLength_atom) {
    facetType = nsISchemaFacet::FACET_TYPE_LENGTH;
  }
  else if (aTagName == nsSchemaAtoms::sMinLength_atom) {
    facetType = nsISchemaFacet::FACET_TYPE_MINLENGTH;
  }
  else if (aTagName == nsSchemaAtoms::sMaxLength_atom) {
    facetType = nsISchemaFacet::FACET_TYPE_MAXLENGTH;
  }
  else if (aTagName == nsSchemaAtoms::sPattern_atom) {
    facetType = nsISchemaFacet::FACET_TYPE_PATTERN;
  }
  else if (aTagName == nsSchemaAtoms::sEnumeration_atom) {
    facetType = nsISchemaFacet::FACET_TYPE_ENUMERATION;
  }
  else if (aTagName == nsSchemaAtoms::sWhiteSpace_atom) {
    facetType = nsISchemaFacet::FACET_TYPE_WHITESPACE;
  }
  else if (aTagName == nsSchemaAtoms::sMaxInclusive_atom) {
    facetType = nsISchemaFacet::FACET_TYPE_MAXINCLUSIVE;
  }
  else if (aTagName == nsSchemaAtoms::sMinInclusive_atom) {
    facetType = nsISchemaFacet::FACET_TYPE_MININCLUSIVE;
  }
  else if (aTagName == nsSchemaAtoms::sMaxExclusive_atom) {
    facetType = nsISchemaFacet::FACET_TYPE_MAXEXCLUSIVE;
  }
  else if (aTagName == nsSchemaAtoms::sMaxInclusive_atom) {
    facetType = nsISchemaFacet::FACET_TYPE_MINEXCLUSIVE;
  }
  else if (aTagName == nsSchemaAtoms::sTotalDigits_atom) {
    facetType = nsISchemaFacet::FACET_TYPE_TOTALDIGITS;
  }
  else if (aTagName == nsSchemaAtoms::sFractionDigits_atom) {
    facetType = nsISchemaFacet::FACET_TYPE_FRACTIONDIGITS;
  }
  facetInst->SetFacetType(facetType);

  nsAutoString valueStr;
  aElement->GetAttribute(NS_LITERAL_STRING("value"), valueStr);
  if (valueStr.Length() == 0) {
    return NS_ERROR_SCHEMA_FACET_VALUE_ERROR;
  }

  if ((aTagName == nsSchemaAtoms::sLength_atom) ||
      (aTagName == nsSchemaAtoms::sMinLength_atom) ||
      (aTagName == nsSchemaAtoms::sMaxLength_atom) ||
      (aTagName == nsSchemaAtoms::sTotalDigits_atom) ||
      (aTagName == nsSchemaAtoms::sFractionDigits_atom)) {
    PRInt32 intVal = valueStr.ToInteger(&rv);

    if (NS_FAILED(rv) ||
        (intVal < 0) ||
        ((aTagName == nsSchemaAtoms::sTotalDigits_atom) && (intVal == 0))) {
      return NS_ERROR_SCHEMA_FACET_VALUE_ERROR;
    }

    facetInst->SetUintValue((PRUint32)intVal);
  }
  else if (aTagName == nsSchemaAtoms::sWhiteSpace_atom) {
    PRUint16 whiteSpaceVal;
    if (valueStr.Equals(NS_LITERAL_STRING("collapse"))) {
      whiteSpaceVal = nsSchemaFacet::WHITESPACE_COLLAPSE;
    }
    else if (valueStr.Equals(NS_LITERAL_STRING("preserve"))) {
      whiteSpaceVal = nsSchemaFacet::WHITESPACE_PRESERVE;
    }
    else if (valueStr.Equals(NS_LITERAL_STRING("replace"))) {
      whiteSpaceVal = nsSchemaFacet::WHITESPACE_REPLACE;
    }
    else {
      return NS_ERROR_SCHEMA_FACET_VALUE_ERROR;
    }

    facetInst->SetWhitespaceValue(whiteSpaceVal);
  }
  else {
    facetInst->SetValue(valueStr);
  }
  
  nsAutoString isFixed;
  aElement->GetAttribute(NS_LITERAL_STRING("fixed"), isFixed);
  facetInst->SetIsFixed(isFixed.Equals(NS_LITERAL_STRING("true")));

  *aFacet = facet;
  NS_ADDREF(*aFacet);

  return NS_OK;
}

void
nsSchemaLoader::GetUse(nsIDOMElement* aElement, 
                       PRUint16* aUse)
{
  *aUse = nsISchemaAttribute::USE_OPTIONAL;

  nsAutoString use;
  aElement->GetAttribute(NS_LITERAL_STRING("use"), use);
  
  if (use.Equals(NS_LITERAL_STRING("prohibited"))) {
    *aUse = nsISchemaAttribute::USE_PROHIBITED;
  }
  else if (use.Equals(NS_LITERAL_STRING("required"))) {
    *aUse = nsISchemaAttribute::USE_REQUIRED;
  }
}

void
nsSchemaLoader::GetProcess(nsIDOMElement* aElement, 
                           PRUint16* aProcess)
{
  *aProcess = nsISchemaAnyParticle::PROCESS_STRICT;

  nsAutoString process;
  aElement->GetAttribute(NS_LITERAL_STRING("process"), process);

  if (process.Equals(NS_LITERAL_STRING("lax"))) {
    *aProcess = nsISchemaAnyParticle::PROCESS_LAX;
  }
  else if (process.Equals(NS_LITERAL_STRING("skip"))) {
    *aProcess = nsISchemaAnyParticle::PROCESS_SKIP;
  }
}

void
nsSchemaLoader::GetMinAndMax(nsIDOMElement* aElement,
                             PRUint32* aMinOccurs,
                             PRUint32* aMaxOccurs)
{
  *aMinOccurs = 1;
  *aMaxOccurs = 1;

  nsAutoString minStr, maxStr;
  aElement->GetAttribute(NS_LITERAL_STRING("minOccurs"), minStr);
  aElement->GetAttribute(NS_LITERAL_STRING("maxOccurs"), maxStr);
  
  PRInt32 rv;
  if (minStr.Length() > 0) {
    PRInt32 minVal = minStr.ToInteger(&rv);
    if (NS_SUCCEEDED(rv) && (minVal >= 0)) {
      *aMinOccurs = (PRUint32)minVal;
    }
  }

  if (maxStr.Length() > 0) {
    if (maxStr.Equals(NS_LITERAL_STRING("unbounded"))) {
      *aMaxOccurs = nsISchemaParticle::OCCURRENCE_UNBOUNDED;
    }
    else {
      PRInt32 maxVal = maxStr.ToInteger(&rv);
      if (NS_SUCCEEDED(rv) && (maxVal >= 0)) {
        *aMaxOccurs = (PRUint32)maxVal;
      }
    }
  }
}

