// Copyright (c) 1994 James Clark
// See the file COPYING for copying permission.

#ifdef __GNUG__
#pragma implementation
#endif
#include "splib.h"
#include "Attribute.h"
#include "MessageArg.h"
#include "macros.h"
#include "ParserMessages.h"
#include "Syntax.h"
#include "Entity.h"
#include "Notation.h"

#ifdef SP_NAMESPACE
namespace SP_NAMESPACE {
#endif

DeclaredValue::DeclaredValue()
{
}

DeclaredValue::~DeclaredValue()
{
}

AttributeValue *DeclaredValue::makeValueFromToken(Text &text,
						  AttributeContext &context,
						  const StringC &name,
						  unsigned &specLength) const
{
  return makeValue(text, context, name, specLength);
}

AttributeSemantics *DeclaredValue::makeSemantics(const TokenizedAttributeValue &,
						 AttributeContext &,
						 const StringC &,
						 unsigned &,
						 unsigned &) const
{
  return 0;
}

Boolean DeclaredValue::containsToken(const StringC &) const
{
  return 0;
}

Boolean DeclaredValue::isNotation() const
{
  return 0;
}

Boolean DeclaredValue::isEntity() const
{
  return 0;
}

Boolean DeclaredValue::isId() const
{
  return 0;
}

Boolean DeclaredValue::isIdref() const
{
  return 0;
}

const Vector<StringC> *DeclaredValue::getTokens() const
{
  return 0;
}


CdataDeclaredValue::CdataDeclaredValue()
{
}

Boolean CdataDeclaredValue::tokenized() const
{
  return 0;
}

void CdataDeclaredValue::checkNormalizedLength(Text &text, 
                                               AttributeContext &context,
                                               unsigned &specLength) const
{
  const Syntax &syntax = context.attributeSyntax();
  size_t normsep = syntax.normsep();
  size_t normalizedLength = text.normalizedLength(normsep);
  specLength += normalizedLength;
  size_t litlen = syntax.litlen();
  // A length error will already have been given if
  // length > litlen - normsep.
  if (litlen >= normsep && text.size() <= litlen - normsep
      && normalizedLength > litlen)
    context.message(ParserMessages::normalizedAttributeValueLength,
		    NumberMessageArg(litlen),
		    NumberMessageArg(normalizedLength));
}

AttributeValue *CdataDeclaredValue::makeValue(Text &text, AttributeContext &context,
					      const StringC &,
					      unsigned &specLength) const
{
  checkNormalizedLength(text, context, specLength);
  return new CdataAttributeValue(text);
}

void CdataDeclaredValue::buildDesc(AttributeDefinitionDesc &desc) const
{
  desc.declaredValue = AttributeDefinitionDesc::cdata;
}

DeclaredValue *CdataDeclaredValue::copy() const
{
  return new CdataDeclaredValue(*this);
}

DataDeclaredValue::DataDeclaredValue(const ConstPtr<Notation> &nt,
                                     AttributeList &attributes)
: notation_(nt)
{
  attributes.swap(attributes_);
}

AttributeValue *DataDeclaredValue::makeValue(Text &text,
                                             AttributeContext &context,
                                             const StringC &,
                                             unsigned &specLength) const
{
  checkNormalizedLength(text, context, specLength);
  return new DataAttributeValue(text, notation_, attributes_);
}

DeclaredValue *DataDeclaredValue::copy() const
{
  return new DataDeclaredValue(*this);
}

TokenizedDeclaredValue::TokenizedDeclaredValue(TokenType type,
					       Boolean isList)
: type_(type), isList_(isList)
{
  switch (type) {
  case name:
  case entityName:
    initialCategories_ = Syntax::nameStartCategory;
    subsequentCategories_ = (Syntax::nameStartCategory|Syntax::digitCategory
			     | Syntax::otherNameCategory);
    break;
  case number:
    initialCategories_ = Syntax::digitCategory;
    subsequentCategories_ = Syntax::digitCategory;
    break;
  case nameToken:
    initialCategories_ = (Syntax::nameStartCategory|Syntax::digitCategory
			  | Syntax::otherNameCategory);
    subsequentCategories_ = initialCategories_;
    break;
  case numberToken:
    initialCategories_ = Syntax::digitCategory;
    subsequentCategories_ = (Syntax::nameStartCategory|Syntax::digitCategory
			     | Syntax::otherNameCategory);
    break;
  }
}

Boolean TokenizedDeclaredValue::tokenized() const
{
  return 1;
}

AttributeValue *TokenizedDeclaredValue::makeValue(Text &text,
						  AttributeContext &context,
						  const StringC &str,
						  unsigned &specLength) const
{
  return makeTokenizedValue(text, context, str, specLength);
}

TokenizedAttributeValue *
TokenizedDeclaredValue::makeTokenizedValue(Text &text,
					   AttributeContext &context,
					   const StringC &name,
					   unsigned &specLength) const
{
  Vector<size_t> spaceIndex;
  const Syntax &syntax = context.attributeSyntax();
  Char space = syntax.space();
  text.subst(*(type_ == entityName
	       ? syntax.entitySubstTable()
	       : syntax.generalSubstTable()),
	     space);
  const StringC &value = text.string();
  size_t i = 0;
  size_t length = value.size();

  for (;;) {
    if (i >= length) {
      // ends with a space (which would have to have been entered
      // via a numeric character reference)
      if (context.validate())
	context.message(ParserMessages::attributeValueSyntax);
      break;
    }
    size_t startIndex = i;
    if (context.validate()) {
      if (!(syntax.charCategory(value[i]) & initialCategories_)) {
        context.Messenger::setNextLocation(text.charLocation(i));
        Char c = value[i];
	if (!(syntax.charCategory(value[i]) & subsequentCategories_))
	  context.message(ParserMessages::attributeValueChar,
	                  StringMessageArg(StringC(&c, 1)),
	                  StringMessageArg(name));
	else if (initialCategories_ == Syntax::digitCategory)
	  context.message(ParserMessages::attributeValueNumberToken,
	                  StringMessageArg(StringC(&c, 1)),
	                  StringMessageArg(name));
	else
	  context.message(ParserMessages::attributeValueName,
	                  StringMessageArg(StringC(&c, 1)),
	                  StringMessageArg(name));
      }
      else {
	for (++i;
             i < length
	     && (syntax.charCategory(value[i]) & subsequentCategories_);
	     i++)
	  ;
	if (i < length && value[i] != space) {
	  Char c = value[i];
	  // character value[i] is not allowed anywhere in the value
	  context.Messenger::setNextLocation(text.charLocation(i));
	  context.message(ParserMessages::attributeValueChar,
		          StringMessageArg(StringC(&c, 1)),
		          StringMessageArg(name));
	}
      }
    }
    while (i < length && value[i] != space)
      i++;
    if (i - startIndex > syntax.namelen()) {
      context.Messenger::setNextLocation(text.charLocation(i));
      context.message(ParserMessages::nameTokenLength,
		      NumberMessageArg(syntax.namelen()));
    }
    if (i == length)
      break;
    if (!isList_ && context.validate() && spaceIndex.size() == 0) {
      context.Messenger::setNextLocation(text.charLocation(i));
      context.message(ParserMessages::attributeValueMultiple,
		      StringMessageArg(name));
    }
    spaceIndex.push_back(i);
    i++;
  }
  size_t normsep = syntax.normsep();
  size_t litlen = syntax.litlen();
  size_t normalizedLength = normsep + length;
  // should we count CDATA and SDATA entities here?
  if (isList_) {
    normalizedLength += 1;
    // length is now the number of characters in each token in the list
    // + 1 for each token in the list; so add normsep - 1 for each
    // token in the list.
    if (normsep > 0)
      normalizedLength += (normsep - 1)*(spaceIndex.size() + 1);
    else
      normalizedLength -= spaceIndex.size() + 1;
  }
  specLength += normalizedLength;
  // A length error will already have been given if
  // length > litlen - normsep.
  if (litlen >= normsep && length <= litlen - normsep
      && normalizedLength > litlen)
    context.message(ParserMessages::normalizedAttributeValueLength,
		    NumberMessageArg(litlen),
		    NumberMessageArg(normalizedLength));
  return new TokenizedAttributeValue(text, spaceIndex);
}

Boolean TokenizedAttributeValue::recoverUnquoted(const StringC &str,
						 const Location &strLoc,
						 AttributeContext &context,
						 const StringC &name)
{
  TextIter iter(text_);
  TextItem::Type type;
  const Char *s;
  size_t len;
  const Location *loc;
  if (iter.next(type, s, len, loc)
      && type == TextItem::data
      && len == text_.size()
      && loc->origin().pointer() == strLoc.origin().pointer()
      && loc->index() + len == strLoc.index()
      && !iter.next(type, s, len, loc)) {
    context.Messenger::setNextLocation(strLoc);
    context.message(ParserMessages::attributeValueChar,
		    StringMessageArg(StringC(str.data(), 1)),
		    StringMessageArg(name));
    return 1;
  }
  return 0;
}

void TokenizedDeclaredValue::buildDesc(AttributeDefinitionDesc &desc) const
{
  desc.declaredValue = AttributeDefinitionDesc::DeclaredValue(
    type_ - name + (isList_
		    ? AttributeDefinitionDesc::names
		    : AttributeDefinitionDesc::name));
}

DeclaredValue *TokenizedDeclaredValue::copy() const
{
  return new TokenizedDeclaredValue(*this);
}

GroupDeclaredValue::GroupDeclaredValue(TokenType type,
				       Vector<StringC> &vec)
: TokenizedDeclaredValue(type, 0)
{
  vec.swap(allowedValues_);
}

void GroupDeclaredValue::buildDesc(AttributeDefinitionDesc &desc) const
{
  desc.allowedValues = allowedValues_;
}

DeclaredValue *GroupDeclaredValue::copy() const
{
  return new GroupDeclaredValue(*this);
}

AttributeValue *GroupDeclaredValue::makeValue(Text &text,
					      AttributeContext &context,
					      const StringC &name,
					      unsigned &specLength) const
{
  TokenizedAttributeValue *val = makeTokenizedValue(text, context, name,
						    specLength);
  if (!val || !context.validate())
    return val;
  for (size_t i = 0; i < allowedValues_.size(); i++)
    if (val->string() == allowedValues_[i])
      return val;
  context.message(ParserMessages::attributeValueNotInGroup,
		  StringMessageArg(val->string()),
		  StringMessageArg(name),
		  StringVectorMessageArg(allowedValues_));
  return val;
}

AttributeValue *GroupDeclaredValue::makeValueFromToken(Text &text,
						       AttributeContext &context,
						       const StringC &,
						       unsigned &specLength)
     const
{
  const Syntax &syntax = context.attributeSyntax();
  size_t litlen = syntax.litlen();
  size_t normsep = syntax.normsep();
  if (normsep > litlen || text.size() >  litlen - normsep)
    context.message(ParserMessages::normalizedAttributeValueLength,
		    NumberMessageArg(litlen),
		    NumberMessageArg(text.size() + normsep));
  specLength += text.size() + normsep;
  return new TokenizedAttributeValue(text, Vector<size_t>());
}

Boolean GroupDeclaredValue::containsToken(const StringC &token) const
{
  for (size_t i = 0; i < allowedValues_.size(); i++)
    if (allowedValues_[i] == token)
      return 1;
  return 0;
}

const Vector<StringC> *GroupDeclaredValue::getTokens() const
{
  return &allowedValues_;
}

NameTokenGroupDeclaredValue::NameTokenGroupDeclaredValue(Vector<StringC> &vec)
: GroupDeclaredValue(nameToken, vec)
{
}

void NameTokenGroupDeclaredValue::buildDesc(AttributeDefinitionDesc &desc) const
{
  GroupDeclaredValue::buildDesc(desc);
  desc.declaredValue = AttributeDefinitionDesc::nameTokenGroup;
}

DeclaredValue *NameTokenGroupDeclaredValue::copy() const
{
  return new NameTokenGroupDeclaredValue(*this);
}

NotationDeclaredValue::NotationDeclaredValue(Vector<StringC> &vec)
: GroupDeclaredValue(name, vec)
{
}

Boolean NotationDeclaredValue::isNotation() const
{
  return 1;
}

AttributeSemantics *
NotationDeclaredValue::makeSemantics(const TokenizedAttributeValue &value,
				     AttributeContext &context,
				     const StringC &,
				     unsigned &,
				     unsigned &) const
{
  ConstPtr<Notation> notation
    = context.getAttributeNotation(value.string(),
				   value.tokenLocation(0));
  if (notation.isNull()) {
    if (context.validate()) {
      context.setNextLocation(value.tokenLocation(0));
      context.message(ParserMessages::invalidNotationAttribute,
		      StringMessageArg(value.string()));
    }
    return 0;
  }
  return new NotationAttributeSemantics(notation);
}

void NotationDeclaredValue::buildDesc(AttributeDefinitionDesc &desc) const
{
  GroupDeclaredValue::buildDesc(desc);
  desc.declaredValue = AttributeDefinitionDesc::notation;
}

DeclaredValue *NotationDeclaredValue::copy() const
{
  return new NotationDeclaredValue(*this);
}

EntityDeclaredValue::EntityDeclaredValue(Boolean isList)
: TokenizedDeclaredValue(entityName, isList)
{
}

Boolean EntityDeclaredValue::isEntity() const
{
  return 1;
}

AttributeSemantics *
EntityDeclaredValue::makeSemantics(const TokenizedAttributeValue &value,
				   AttributeContext &context,
				   const StringC &,
				   unsigned &,
				   unsigned &nEntityNames) const
{
  Boolean valid = 1;
  size_t nTokens = value.nTokens();
  nEntityNames += nTokens;
  Vector<ConstPtr<Entity> > entities(nTokens);
  for (size_t i = 0; i < nTokens; i++) {
    entities[i] = context.getAttributeEntity(value.token(i),
					     value.tokenLocation(i));
    if (entities[i].isNull()) {
      if (context.validate()) {
	context.setNextLocation(value.tokenLocation(i));
	context.message(ParserMessages::invalidEntityAttribute,
		        StringMessageArg(value.token(i)));
      }
      valid = 0;
    }
    else if (!entities[i]->isDataOrSubdoc()) {
      if (context.validate()) {
        context.Messenger::setNextLocation(value.tokenLocation(i));
        context.message(ParserMessages::notDataOrSubdocEntity,
		        StringMessageArg(value.token(i)));
      }
      valid = 0;
    }
  }
  if (valid)
    return new EntityAttributeSemantics(entities);
  else
    return 0;
}

DeclaredValue *EntityDeclaredValue::copy() const
{
  return new EntityDeclaredValue(*this);
}

IdDeclaredValue::IdDeclaredValue()
: TokenizedDeclaredValue(name, 0)
{
}

Boolean IdDeclaredValue::isId() const
{
  return 1;
}

AttributeSemantics *
IdDeclaredValue::makeSemantics(const TokenizedAttributeValue &value,
			       AttributeContext &context,
			       const StringC &,
			       unsigned &,
			       unsigned &) const
{
  Location prevLoc;
  if (!context.defineId(value.string(), value.tokenLocation(0), prevLoc)) {
    context.setNextLocation(value.tokenLocation(0));
    context.message(ParserMessages::duplicateId,
		    StringMessageArg(value.string()),
		    prevLoc);
  }
  return 0;
}

void IdDeclaredValue::buildDesc(AttributeDefinitionDesc &desc) const
{
  desc.declaredValue = AttributeDefinitionDesc::id;
}

DeclaredValue *IdDeclaredValue::copy() const
{
  return new IdDeclaredValue(*this);
}

IdrefDeclaredValue::IdrefDeclaredValue(Boolean isList)
: TokenizedDeclaredValue(name, isList)
{
}

AttributeSemantics *
IdrefDeclaredValue::makeSemantics(const TokenizedAttributeValue &value,
				  AttributeContext &context,
				  const StringC &,
				  unsigned &nIdrefs,
				  unsigned &) const
{
  size_t nTokens = value.nTokens();
  nIdrefs += nTokens;
  for (size_t i = 0; i < nTokens; i++)
    context.noteIdref(value.token(i), value.tokenLocation(i));
  return 0;
}

Boolean IdrefDeclaredValue::isIdref() const
{
  return 1;
}

void IdrefDeclaredValue::buildDesc(AttributeDefinitionDesc &desc) const
{
  TokenizedDeclaredValue::buildDesc(desc);
  if (desc.declaredValue == AttributeDefinitionDesc::name)
    desc.declaredValue = AttributeDefinitionDesc::idref;
  else
    desc.declaredValue = AttributeDefinitionDesc::idrefs;
}

DeclaredValue *IdrefDeclaredValue::copy() const
{
  return new IdrefDeclaredValue(*this);
}


AttributeDefinition::AttributeDefinition(const StringC &name,
					 DeclaredValue *value)
: name_(name), declaredValue_(value)
{
}

AttributeDefinition::~AttributeDefinition()
{
}

AttributeValue *AttributeDefinition::checkValue(AttributeValue *p,
						AttributeContext &) const
{
  return p;
}

Boolean AttributeDefinition::missingValueWouldMatch(const Text &,
						    const AttributeContext &) const
{
  return 0;
}

const AttributeValue *
AttributeDefinition::defaultValue(const AttributeValue *) const
{
  return 0;
}

void AttributeDefinition::getDesc(AttributeDefinitionDesc &desc) const
{
  desc.allowedValues.clear();
  desc.defaultValue.clear();
  desc.currentIndex = 0;
  buildDesc(desc);
  declaredValue_->buildDesc(desc);
}

Boolean AttributeDefinition::isConref() const
{
  return 0;
}

Boolean AttributeDefinition::isCurrent() const
{
  return 0;
}

Boolean AttributeDefinition::isFixed() const
{
  return 0;
}

RequiredAttributeDefinition::RequiredAttributeDefinition(const StringC &name,
							 DeclaredValue *value)
: AttributeDefinition(name, value)
{
}

ConstPtr<AttributeValue>
RequiredAttributeDefinition::makeMissingValue(AttributeContext &context) const
{
  if (context.validate())
    context.message(ParserMessages::requiredAttributeMissing,
		    StringMessageArg(name()));
  return 0;
}

void RequiredAttributeDefinition::buildDesc(AttributeDefinitionDesc &desc) const
{
  desc.defaultValueType = AttributeDefinitionDesc::required;
}

AttributeDefinition *RequiredAttributeDefinition::copy() const
{
  return new RequiredAttributeDefinition(*this);
}

CurrentAttributeDefinition::CurrentAttributeDefinition(const StringC &name, DeclaredValue *value, size_t index)
: AttributeDefinition(name, value), currentIndex_(index)
{
}

ConstPtr<AttributeValue>
CurrentAttributeDefinition::makeMissingValue(AttributeContext &context) const
{
  if (context.mayDefaultAttribute()) {
    ConstPtr<AttributeValue> currentValue
      = context.getCurrentAttribute(currentIndex_);
    if (currentValue.isNull() && context.validate())
      context.message(ParserMessages::currentAttributeMissing,
		      StringMessageArg(name()));
    return currentValue;
  }
  if (context.validate())
    context.message(ParserMessages::attributeMissing,
		    StringMessageArg(name()));
  return 0;
}

Boolean CurrentAttributeDefinition::missingValueWouldMatch(const Text &text,
							   const AttributeContext &context) const
{
  if (!context.mayDefaultAttribute())
    return 0;
  ConstPtr<AttributeValue> currentValue
    = context.getCurrentAttribute(currentIndex_);
  if (currentValue.isNull())
    return 0;
  return text.fixedEqual(*currentValue->text());
}

AttributeValue *
CurrentAttributeDefinition::checkValue(AttributeValue *value,
				       AttributeContext &context) const
{
  context.noteCurrentAttribute(currentIndex_, value);
  return value;
}

void CurrentAttributeDefinition::buildDesc(AttributeDefinitionDesc &desc) const
{
  desc.defaultValueType = AttributeDefinitionDesc::current;
  desc.currentIndex = currentIndex_;
}

AttributeDefinition *CurrentAttributeDefinition::copy() const
{
  return new CurrentAttributeDefinition(*this);
}

Boolean CurrentAttributeDefinition::isCurrent() const
{
  return 1;
}

ImpliedAttributeDefinition::ImpliedAttributeDefinition(const StringC &name,
						       DeclaredValue *value)
: AttributeDefinition(name, value)
{
}

ConstPtr<AttributeValue>
ImpliedAttributeDefinition::makeMissingValue(AttributeContext &context) const
{
  return context.makeImpliedAttributeValue();
}

void ImpliedAttributeDefinition::buildDesc(AttributeDefinitionDesc &desc) const
{
  desc.defaultValueType = AttributeDefinitionDesc::implied;
}

AttributeDefinition *ImpliedAttributeDefinition::copy() const
{
  return new ImpliedAttributeDefinition(*this);
}

const AttributeValue *
ImpliedAttributeDefinition::defaultValue(const AttributeValue *impliedValue)
     const
{
  return impliedValue;
}

ConrefAttributeDefinition::ConrefAttributeDefinition(const StringC &name,
						     DeclaredValue *value)
: ImpliedAttributeDefinition(name, value)
{
}

Boolean ConrefAttributeDefinition::isConref() const
{
  return 1;
}

void ConrefAttributeDefinition::buildDesc(AttributeDefinitionDesc &desc) const
{
  desc.defaultValueType = AttributeDefinitionDesc::conref;
}

AttributeDefinition *ConrefAttributeDefinition::copy() const
{
  return new ConrefAttributeDefinition(*this);
}

DefaultAttributeDefinition::DefaultAttributeDefinition(const StringC &name,
							DeclaredValue *declaredValue,
							AttributeValue *defaultValue)
: AttributeDefinition(name, declaredValue),
  value_(defaultValue)
{
}

ConstPtr<AttributeValue>
DefaultAttributeDefinition::makeMissingValue(AttributeContext &context) const
{
  if (context.mayDefaultAttribute())
    return value_;
  if (context.validate())
    context.message(ParserMessages::attributeMissing,
		    StringMessageArg(name()));
  return 0;
}

Boolean DefaultAttributeDefinition::missingValueWouldMatch(const Text &text,
							   const AttributeContext &context) const
{
  return context.mayDefaultAttribute() && text.fixedEqual(*value_->text());
}

void DefaultAttributeDefinition::buildDesc(AttributeDefinitionDesc &desc) const
{
  desc.defaultValueType = AttributeDefinitionDesc::defaulted;
  desc.defaultValue = value_;
}

AttributeDefinition *DefaultAttributeDefinition::copy() const
{
  return new DefaultAttributeDefinition(*this);
}

FixedAttributeDefinition:: FixedAttributeDefinition(const StringC &name,
						    DeclaredValue *declaredValue,
						    AttributeValue *defaultValue)
: DefaultAttributeDefinition(name, declaredValue, defaultValue)
{
}

Boolean FixedAttributeDefinition::isFixed() const
{
  return 1;
}

AttributeValue *FixedAttributeDefinition::checkValue(AttributeValue *value,
						     AttributeContext &context)
     const
{
  const AttributeValue *fixedValue
    = DefaultAttributeDefinition::defaultValue(0);
  if (value && fixedValue && context.validate()) {
    const Text *text;
    const StringC *str;
    const Text *fixedText;
    const StringC *fixedStr;
    switch (value->info(text, str)) {
    case AttributeValue::implied:
      CANNOT_HAPPEN();
    case AttributeValue::cdata:
      if (fixedValue->info(fixedText, fixedStr) == AttributeValue::cdata) {
	if (!text->fixedEqual(*fixedText))
	  context.message(ParserMessages::notFixedValue, StringMessageArg(name()));
      }
      break;
    case AttributeValue::tokenized:
      if (fixedValue->info(fixedText, fixedStr) == AttributeValue::tokenized) {
	if (*str != *fixedStr)
	  context.message(ParserMessages::notFixedValue, StringMessageArg(name()));
      }
      break;
    }
  }
  return value;
}

void FixedAttributeDefinition::buildDesc(AttributeDefinitionDesc &desc) const
{
  // get the fixed value
  DefaultAttributeDefinition::buildDesc(desc);
  desc.defaultValueType = AttributeDefinitionDesc::fixed;
}

AttributeDefinition *FixedAttributeDefinition::copy() const
{
  return new FixedAttributeDefinition(*this);
}

AttributeDefinitionList
::AttributeDefinitionList(Vector<CopyOwner<AttributeDefinition> > &vec,
			  size_t index,
			  Boolean anyCurrent,
			  size_t idIndex,
			  size_t notationIndex)
: index_(index), anyCurrent_(anyCurrent), idIndex_(idIndex),
  notationIndex_(notationIndex)
{
  defs_.swap(vec);
}

AttributeDefinitionList:: AttributeDefinitionList(const ConstPtr<AttributeDefinitionList> &def)
: prev_(def), index_(size_t(-1))
{
  if (def.isNull()) {
    anyCurrent_ = 0;
    notationIndex_ = size_t(-1);
    idIndex_ = size_t(-1);
  }
  else {
    anyCurrent_ = def->anyCurrent_;
    notationIndex_ = def->notationIndex_;
    idIndex_ = def->idIndex_;
    defs_ = def->defs_;
  }
}

Boolean AttributeDefinitionList::tokenIndex(const StringC &token, unsigned &index) const
{
  for (size_t i = 0; i < defs_.size(); i++)
    if (defs_[i]->containsToken(token)) {
      index = i;
      return 1;
    }
  return 0;
}

Boolean AttributeDefinitionList::tokenIndexUnique(const StringC &token, unsigned i) const
{
  for (++i; i < defs_.size(); i++)
    if (defs_[i]->containsToken(token))
      return 0;
  return 1;
}


Boolean AttributeDefinitionList::attributeIndex(const StringC &name,
						unsigned &index) const
{
  for (size_t i = 0; i < defs_.size(); i++)
    if (defs_[i]->name() == name) {
      index = i;
      return 1;
    }
  return 0;
}

void AttributeDefinitionList::append(AttributeDefinition *def)
{
  if (def->isId() && idIndex_ == size_t(-1))
    idIndex_ = defs_.size();
  if (def->isNotation() && notationIndex_ == size_t(-1))
    notationIndex_ = defs_.size();
  if (def->isCurrent())
    anyCurrent_ = 1;
  defs_.resize(defs_.size() + 1);
  defs_.back() = def;
}

AttributeSemantics::AttributeSemantics()
{
}

AttributeSemantics::~AttributeSemantics()
{
}

size_t AttributeSemantics::nEntities() const
{
  return 0;
}

ConstPtr<Entity> AttributeSemantics::entity(size_t) const
{
  return 0;
}

ConstPtr<Notation> AttributeSemantics::notation() const
{
  return 0;
}


NotationAttributeSemantics::NotationAttributeSemantics(const ConstPtr<Notation> &notation)
: notation_(notation)
{
}

ConstPtr<Notation> NotationAttributeSemantics::notation() const
{
  return notation_;
}

AttributeSemantics *NotationAttributeSemantics::copy() const
{
  return new NotationAttributeSemantics(*this);
}

EntityAttributeSemantics::EntityAttributeSemantics(Vector<ConstPtr<Entity> > &entity)
{
  entity.swap(entity_);
}

size_t EntityAttributeSemantics::nEntities() const
{
  return entity_.size();
}

ConstPtr<Entity> EntityAttributeSemantics::entity(size_t i) const
{
  return entity_[i];
}

AttributeSemantics *EntityAttributeSemantics::copy() const
{
  return new EntityAttributeSemantics(*this);
}

AttributeValue::AttributeValue()
{
}

AttributeValue::~AttributeValue()
{
}

AttributeSemantics *AttributeValue::makeSemantics(const DeclaredValue *,
						  AttributeContext &,
						  const StringC &,
						  unsigned &,
						  unsigned &) const
{
  return 0;
}

const Text *AttributeValue::text() const
{
  return 0;
}

Boolean AttributeValue::recoverUnquoted(const StringC &, const Location &,
					AttributeContext &, const StringC &)
{
  return 0;
}

ImpliedAttributeValue::ImpliedAttributeValue()
{
}

AttributeValue::Type ImpliedAttributeValue::info(const Text *&,
						 const StringC *&) const
{
  return implied;
}

TokenizedAttributeValue::TokenizedAttributeValue(Text &text,
						 const Vector<size_t> &spaceIndex)
: spaceIndex_(spaceIndex)
{
  text.swap(text_);
}

AttributeValue::Type TokenizedAttributeValue::info(const Text *&,
						   const StringC *&string) const
{
  string = &text_.string();
  return tokenized;
}

const Text *TokenizedAttributeValue::text() const
{
  return &text_;
}

AttributeSemantics *
TokenizedAttributeValue::makeSemantics(const DeclaredValue *value,
				       AttributeContext &context,
				       const StringC &name,
				       unsigned &nIdrefs,
				       unsigned &nEntityNames) const
{
  if (text_.size() == 0)
    return 0;
  return value->makeSemantics(*this, context, name, nIdrefs, nEntityNames);
}

CdataAttributeValue::CdataAttributeValue(Text &text)
{
  text.swap(text_);
}

AttributeValue::Type CdataAttributeValue::info(const Text *&text,
					       const StringC *&) const
{
  text = &text_;
  return cdata;
}

const Text *CdataAttributeValue::text() const
{
  return &text_;
}

Boolean CdataAttributeValue::recoverUnquoted(const StringC &str,
					     const Location &strLoc,
					     AttributeContext &context,
					     const StringC &)
{
  TextIter iter(text_);
  TextItem::Type type;
  const Char *s;
  size_t len;
  const Location *loc;
  if (iter.next(type, s, len, loc)
      && type == TextItem::data
      && len == text_.size()
      && loc->origin().pointer() == strLoc.origin().pointer()
      && loc->index() + len == strLoc.index()
      && !iter.next(type, s, len, loc)) {
    text_.addChars(str, strLoc);
    context.Messenger::setNextLocation(strLoc);
    context.message(ParserMessages::unquotedAttributeValue);
    return 1;
  }
  return 0;
}

const Notation *CdataAttributeValue::notation() const
{
  return 0;
}

DataAttributeValue::DataAttributeValue(Text &text,
                                       const ConstPtr<Notation> &nt,
                                       const AttributeList &attributes)
: CdataAttributeValue(text), notation_(nt), attributes_(&attributes)
{
}

const AttributeList &DataAttributeValue::attributes() const
{
  return *attributes_;
}

const Notation *DataAttributeValue::notation() const
{
  return notation_.pointer();
}

Attribute::Attribute()
: specIndexPlus_(0)
{
}

void Attribute::clear()
{
  specIndexPlus_ = 0;
  value_.clear();
  semantics_.clear();
}

AttributeList::AttributeList(const ConstPtr<AttributeDefinitionList> &def)
: def_(def), vec_(def.isNull() ? 0 : def->size()), nSpec_(0), conref_(0),
  nIdrefs_(0), nEntityNames_(0)
{
}

AttributeList::AttributeList()
: nSpec_(0), conref_(0)
{
}

void AttributeList::init(const ConstPtr<AttributeDefinitionList> &def)
{
  def_ = def;
  nSpec_ = 0;
  conref_ = 0;
  nIdrefs_ = 0;
  nEntityNames_ = 0;
  if (def_.isNull())
    vec_.resize(0);
  else {
    size_t newLength = def_->size();
    size_t clearLim = vec_.size();
    if (clearLim > newLength)
      clearLim = newLength;
    vec_.resize(newLength);
    for (size_t i = 0; i < clearLim; i++)
      vec_[i].clear();
  }
}

void AttributeList::changeDef(const ConstPtr<AttributeDefinitionList> &def)
{
  vec_.resize(def.isNull() ? 0 : def->size());
  def_ = def;
}

void AttributeList::swap(AttributeList &to)
{
  vec_.swap(to.vec_);
  def_.swap(to.def_);
  {
    unsigned tem = to.nIdrefs_;
    to.nIdrefs_ = nIdrefs_;
    nIdrefs_ = tem;
  }
  {
    unsigned tem = to.nEntityNames_;
    to.nEntityNames_ = nEntityNames_;
    nEntityNames_ = tem;
  }
  {
    size_t tem = to.nSpec_;
    to.nSpec_ = nSpec_;
    nSpec_ = tem;
  }
  {
    PackedBoolean tem = to.conref_;
    to.conref_ = conref_;
    conref_ = tem;
  }
}

void AttributeList::finish(AttributeContext &context)
{
  for (size_t i = 0; i < vec_.size(); i++)
    if (!vec_[i].specified()) {
      ConstPtr<AttributeValue> value
	= def(i)->makeMissingValue(context);
      if (!conref_ || def_->notationIndex() != i) {
	vec_[i].setValue(value);
        if (!value.isNull())
      	  vec_[i].setSemantics(def(i)->makeSemantics(value.pointer(),
						     context,
						     nIdrefs_,
						     nEntityNames_));
      }
    }
  const Syntax &syntax = context.attributeSyntax();
  if (nIdrefs_ > syntax.grpcnt())
    context.message(ParserMessages::idrefGrpcnt,
		   NumberMessageArg(syntax.grpcnt()));
  if (nEntityNames_ > syntax.grpcnt())
    context.message(ParserMessages::entityNameGrpcnt,
		   NumberMessageArg(syntax.grpcnt()));
  if (context.validate()
      && conref_
      && def_->notationIndex() != size_t(-1)
      && specified(def_->notationIndex()))
    context.message(ParserMessages::conrefNotation);
}

void AttributeList::setSpec(unsigned i, AttributeContext &context)
{
  if (vec_[i].specified())
    context.message(ParserMessages::duplicateAttributeSpec,
		   StringMessageArg(def(i)->name()));
  else
    vec_[i].setSpec(nSpec_++);
}

void AttributeList::noteInvalidSpec()
{
  // This is needed for error recovery.
  // We don't want nSpec_ to be > 0, if there is no attribute definition.
  if (nSpec_)
    nSpec_++;
}

Boolean AttributeList::setValue(unsigned i, Text &text,
				AttributeContext &context,
				unsigned &specLength)
{
  AttributeValue *value = def(i)->makeValue(text, context, specLength);
  if (def(i)->isConref())
    conref_ = 1;
  vec_[i].setValue(value);
  if (value)
    vec_[i].setSemantics(def(i)->makeSemantics(value, context,
					       nIdrefs_, nEntityNames_));
  else if (AttributeValue::handleAsUnterminated(text, context))
    return 0;
  return 1;
}

void AttributeList::setValueToken(unsigned i, Text &text,
				  AttributeContext &context,
				  unsigned &specLength)
{
  AttributeValue *value = def(i)->makeValueFromToken(text, context,
						     specLength);
  if (def(i)->isConref())
    conref_ = 1;
  vec_[i].setValue(value);
  if (value)
    vec_[i].setSemantics(def(i)->makeSemantics(value, context,
					       nIdrefs_, nEntityNames_));
}

const StringC *AttributeList::getId() const
{
  // Check for no attributes
  if (def_.isNull())
    return 0;
  // Check for no ID declared
  size_t i = def_->idIndex();
  if (i == size_t(-1))
    return 0;
  // Check for invalid value
  const AttributeValue *v = value(i);
  if (!v)
    return 0;
  // Check for implied value
  const Text *t = v->text();
  if (!t)
    return 0;
  return &t->string();
}

Boolean AttributeList::recoverUnquoted(const StringC &str,
				       const Location &strLoc,
				       AttributeContext &context)
{
  if (nSpec_ > 0) {
    for (size_t i = 0; i < vec_.size(); i++)
      if (vec_[i].specified() && vec_[i].specIndex() == nSpec_ - 1) {
	const AttributeValue *val = vec_[i].value();
	if (val)
	  // I wish I could avoid casting away const here.
	  return ((AttributeValue *)val)->recoverUnquoted(str, strLoc, context,
							  name(i));
	break;
      }
    return 1;
  }
  return 0;
}

Boolean AttributeList::handleAsUnterminated(AttributeContext &context)
{
  if (nSpec_ > 0) {
    for (size_t i = 0; i < vec_.size(); i++) {
      if (vec_[i].specified() && vec_[i].specIndex() == nSpec_ - 1) {
	const AttributeValue *val = vec_[i].value();
	const Text *ptr;
	if (val && (ptr = val->text()) != 0
	    && AttributeValue::handleAsUnterminated(*ptr, context))
	  return 1;
	break;
      }
    }
  }
  return 0;
}

// This tries to guess this attribute value looks like if it had
// a missing ending quote.

Boolean AttributeValue::handleAsUnterminated(const Text &text,
					     AttributeContext &context)
{
  TextIter iter(text);
  const Char *lastStr = 0;
  size_t lastLen;
  Location startLoc;
  const Location *loc;
  TextItem::Type type;
  const Char *str;
  size_t len;
  while (iter.next(type, str, len, loc)) {
    if (startLoc.origin().isNull() && !loc->origin().isNull())
      startLoc = *loc;
    switch (type) {
    case TextItem::data:
      if (len != 1 || *str != context.attributeSyntax().space()) {
	lastStr = str;
	lastLen = len;
      }
      break;
    case TextItem::endDelim:
    case TextItem::endDelimA:
    case TextItem::ignore:
      break;
    default:
      lastStr = 0;
      break;
    }
  }
  if (lastStr) {
    while (lastLen > 0
	   && lastStr[lastLen - 1] == context.attributeSyntax().space())
      lastLen--;
    const StringC &vi = context.attributeSyntax().delimGeneral(Syntax::dVI);
    if (lastLen >= vi.size()
	&& (vi
	    == StringC(lastStr + (lastLen - vi.size()), vi.size()))) {
      context.Messenger::setNextLocation(startLoc);
      context.message(ParserMessages::literalClosingDelimiter);
      return 1;
    }
  }
  return 0;
}

AttributeContext::AttributeContext()
: mayDefaultAttribute_(0), validate_(1)
{
}

AttributeContext::~AttributeContext()
{
}

Boolean AttributeContext::defineId(const StringC &, const Location &,
				   Location &)
{
  return 1;
}

void AttributeContext::noteIdref(const StringC &, const Location &)
{
}

void AttributeContext::noteCurrentAttribute(size_t, AttributeValue *)
{
}

ConstPtr<AttributeValue> AttributeContext::getCurrentAttribute(size_t) const
{
  return 0;
}

ConstPtr<Entity> AttributeContext::getAttributeEntity(const StringC &,
						      const Location &)
{
  return 0;
}

ConstPtr<Notation> AttributeContext::getAttributeNotation(const StringC &,
							  const Location &)
{
  return 0;
}

ConstPtr<AttributeValue> AttributeContext::makeImpliedAttributeValue()
{
  if (impliedAttributeValue_.isNull())
    impliedAttributeValue_ = new ImpliedAttributeValue;
  return impliedAttributeValue_;
}

#ifdef SP_NAMESPACE
}
#endif
