/*
This product contains certain software code or other information
("AT&T Software") proprietary to AT&T Corp. ("AT&T").  The AT&T
Software is provided to you "AS IS".  YOU ASSUME TOTAL RESPONSIBILITY
AND RISK FOR USE OF THE AT&T SOFTWARE.  AT&T DOES NOT MAKE, AND
EXPRESSLY DISCLAIMS, ANY EXPRESS OR IMPLIED WARRANTIES OF ANY KIND
WHATSOEVER, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, WARRANTIES OF
TITLE OR NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS, ANY
WARRANTIES ARISING BY USAGE OF TRADE, COURSE OF DEALING OR COURSE OF
PERFORMANCE, OR ANY WARRANTY THAT THE AT&T SOFTWARE IS "ERROR FREE" OR
WILL MEET YOUR REQUIREMENTS.

Unless you accept a license to use the AT&T Software, you shall not
reverse compile, disassemble or otherwise reverse engineer this
product to ascertain the source code for any AT&T Software.

(c) AT&T Corp. All rights reserved.  AT&T is a registered trademark of AT&T Corp.

***********************************************************************

History:

      24/11/99  - initial release by Hartmut Liefke, liefke@seas.upenn.edu
                                     Dan Suciu,      suciu@research.att.com
*/

//**************************************************************************
//**************************************************************************

// This module contains the Division-Compressor 'seq'
// The Div-compressor compresses a string by applying a sequence of subcompressors
// to the string. The substrings for the compressors are separated by some delimiter strings

#include "stdafx.h"

#ifdef NOTHREAD
void *DivCompressorItem::operator new(size_t size)  
{  
	return session->mainmem->GetByteBlock(size);
}
void DivCompressorItem::operator delete(void *ptr) {}
#endif

void DivSepComprInfo::CreateSubCompressors(char *paramstr,int len)
   // Generates the subcompressors from the parameter string
{
   char *endstringptr;
   DivCompressorItem **curitemref;

   char  *ptr=paramstr;
   char  *endptr=paramstr+len;

   if(*ptr=='"')  // Did we find a separator string at the beginning ?
   {
      endstringptr=::ParseString(ptr,ptr+len);

      startseparator=ptr+1;
      startseparatorlen=endstringptr-ptr-1;

      ptr=endstringptr+1;
   }
   else  // Otherwise, we don't have any start separator
   {
      startseparator=NULL;
      startseparatorlen=0;
   }

   // We now parse the subcompressors and separator strings

   curitemref=&subcompressors;
   *curitemref=NULL;
   
   do
   {
      ptr=SkipWhiteSpaces(ptr,endptr);
      if(ptr==endptr)   // Did we reach the end?
         break;

      // First, we identify a compressor

      *curitemref=new DivCompressorItem();

		if (session->do_compression) {
	      (*curitemref)->usercompressor=session->compressman->CreateCompressorInstance(ptr,endptr);
		} else {
	      (*curitemref)->useruncompressor=session->decompressman->CreateUncompressorInstance(ptr,endptr);
		}

      (*curitemref)->next=NULL;

      // Next, we identify a delimiter string

      ptr=SkipWhiteSpaces(ptr,endptr);
      if(ptr==endptr)   // Did we reach the end?
         break;

      // There must be string afterwards
		if (!(endstringptr=::ParseString(ptr,endptr))) {
			 XMillException *e = new XMillException(XMILL_ERR_PARSE, "expected a separator constant at ");
			 e->ErrorCont(ptr, endptr-ptr+1);
			 throw e;
		}
		
      // Let's store the separator string
      (*curitemref)->afterseparator=ptr+1;
      (*curitemref)->afterseparatorlen=endstringptr-ptr-1;

      ptr=endstringptr+1;

      curitemref=&((*curitemref)->next);
      *curitemref=NULL;
   }
   while(1);

   if(*curitemref!=NULL)
   {
      (*curitemref)->afterseparator=NULL;
      (*curitemref)->afterseparatorlen=0;
   }
}

// The 'seq' compressor
void DivSepCompressor::ComputeProperties()
{
   isrejecting=1;canoverlap=1;
   isfixedlen=1;
   datasize=0;
   contnum=0;

   DivCompressorItem *item=info.subcompressors;
   while(item!=NULL)
   {
      datasize+=item->usercompressor->GetUserDataSize();
      contnum+=item->usercompressor->GetUserContNum();

      if(item->usercompressor->CanOverlap()==0)
         canoverlap=0;

      // If one of the subcompressors is not fixed-length, then the entire
      // compressor is not fixed-length
      if(item->usercompressor->IsFixedLen()==0)
         isfixedlen=0;

      item=item->next;
   }
}

void DivSepCompressor::CreateSubCompressors(char *paramstr,int len)
{
   info.CreateSubCompressors(paramstr,len);
}

void DivSepCompressor::InitCompress(CompressContainer *cont,char *dataptr)
   // Initializes the the compressors
{
   DivCompressorItem *item=info.subcompressors;

   while(item!=NULL)
   {
      item->usercompressor->InitCompress(cont,dataptr);
      cont+=item->usercompressor->GetUserContNum();
      dataptr+=item->usercompressor->GetUserDataSize();
      item=item->next;
   }
}

char DivSepCompressor::ParseString(char *str,unsigned len,char *dataptr)
{
   char  *ptr1=str,
         *ptr2,*curptr,*endptr,*itemptr;
   DivCompressorItem *item;
   unsigned i;

   // Do we have a start separator ?
   if(info.startseparator!=NULL)
   {
      ptr1=str;
      ptr2=info.startseparator;

      // If the the entire string is shorter than the separator length,
      // the string can definitely not match
      if(len<info.startseparatorlen)
         return 0;

      // Let's check whether the strings are equal
      for(i=0;i<info.startseparatorlen;i++)
      {
         if(*ptr1!=*ptr2)
            return 0;
         ptr1++;
         ptr2++;
      }
      itemptr=ptr1;
   }
   else
      itemptr=str;

   item=info.subcompressors;

   // As long as we have more subcompressors, we continue parsing
   // If 'item->next' is NULL, then the subcompressor in 'item->usercompressor'
   // must match the *rest* of the string and this is done outside of the loop.
   while(item->next!=NULL)
   {
      curptr=itemptr;
      endptr=str+len-item->afterseparatorlen;   // The endptr is the end of the entire string
                                                // minus the length of the separator string

      // We search for the separator
      while(curptr<endptr)
      {
         ptr1=curptr;
         ptr2=item->afterseparator;

         for(i=0;i<item->afterseparatorlen;i++)
         {
            if(*ptr1!=*ptr2)
               break;
            ptr1++;
            ptr2++;
         }
         // We found the separator ?
         if(i==item->afterseparatorlen)
            break;

         curptr++;
      }

      if(curptr>=endptr)
         // We didn't find the separator ? => Exit
         return 0;

      // We keep the length of the string up to the delimiter
      // This will be used in 'CompressString' later.
      item->curitemlen=curptr-itemptr;

      // Let's see if the subcompressor can parse the string
      if(item->usercompressor->ParseString(itemptr,curptr-itemptr,dataptr)==0)
         return 0;

      dataptr+=item->usercompressor->GetUserDataSize();

      itemptr=ptr1;
      item=item->next;
   }

   endptr=str+len;

   // If we have a delimiter at the end, then we check whether it matches
   // the end of 'str'
   if(item->afterseparator!=NULL)
   {
      ptr1=endptr-item->afterseparatorlen;
      if(ptr1<itemptr)
         return 0;
      ptr2=item->afterseparator;

      for(i=0;i<item->afterseparatorlen;i++)
      {
         if(*ptr1!=*ptr2)  // Tail delimiter doesn't match?
            return 0;
         ptr1++;
         ptr2++;
      }
      endptr-=item->afterseparatorlen;
   }

   item->curitemlen=endptr-itemptr;

   // Let's do the parsing for the last subcompressor
   if(item->usercompressor->ParseString(itemptr,endptr-itemptr,dataptr)==0)
      return 0;

   return 1;
}

void DivSepCompressor::CompressString(char *str,unsigned len,CompressContainer *cont,char *dataptr)
   // Compresses the given input string
   // 'dataptr' denotes the state
   // Since the compressor is 'rejecting', the function can expect
   // that 'ParseString' has been called before and
   // 'item->curitemlen' has been set for each subcompressor
{
   DivCompressorItem *item=info.subcompressors;
   char              *startitemptr;

   // Skip the first separator
   startitemptr=str+info.startseparatorlen;

   while(item!=NULL)
   {
      // Compress using the next subcompressor and the length stored
      // in 'item->curitemlen'
      item->usercompressor->CompressString(startitemptr,item->curitemlen,cont,dataptr);

      cont+=item->usercompressor->GetUserContNum();
      dataptr+=item->usercompressor->GetUserDataSize();

      // Skip the next separator and go to the next 
      startitemptr+=item->curitemlen+item->afterseparatorlen;
      item=item->next;
   }
}

void DivSepCompressor::FinishCompress(CompressContainer *cont,char *dataptr)
   // Finishes the compression - the compressor should write any
   // remaining data to the containers
   // For the seq-compressor, all subcompressors are 'finished'
{
   DivCompressorItem *item=info.subcompressors;

   while(item!=NULL)
   {
      item->usercompressor->FinishCompress(cont,dataptr);

      cont+=item->usercompressor->GetUserContNum();
      dataptr+=item->usercompressor->GetUserDataSize();

      item=item->next;
   }
}

void DivSepCompressor::PrintCompressInfo(char *dataptr,unsigned long *overalluncomprsize,unsigned long *overallcomprsize)
   // Prints statistical information about how well the compressor compressed
   // the data
{
   DivCompressorItem *item=info.subcompressors;

   while(item!=NULL)
   {
      item->usercompressor->PrintCompressInfo(dataptr,overalluncomprsize,overallcomprsize);

      dataptr+=item->usercompressor->GetUserDataSize();

      item=item->next;
   }
}

//** The Decompressor ********
void DivSepUncompressor::ComputeProperties()
   // Computes the properties of the decompressor
{
   DivCompressorItem *item=info.subcompressors;

   datasize=0;
   contnum=0;

   while(item!=NULL)
   {
      datasize+=item->useruncompressor->GetUserDataSize();
      contnum+=item->useruncompressor->GetUserContNum();
      item=item->next;
   }
}

void DivSepUncompressor::CreateSubCompressors(char *paramstr,int len)
   // Creates the subcompressor structures based the
   // parameter string
{
   info.CreateSubCompressors(paramstr,len);
}

void DivSepUncompressor::InitUncompress(UncompressContainer *cont,char *dataptr)
   // Initializes the subcompressors
{
   DivCompressorItem *item=info.subcompressors;

   while(item!=NULL)
   {
      item->useruncompressor->InitUncompress(cont,dataptr);
      cont+=item->useruncompressor->GetUserContNum();
      dataptr+=item->useruncompressor->GetUserDataSize();
      item=item->next;
   }
}

void DivSepUncompressor::UncompressItem(UncompressContainer *cont,char *dataptr,XMLOutput *output)
   // Does the actual decompression of a single text item
   // and prints the text to 'output'
{
   DivCompressorItem *item=info.subcompressors;

   // We output the start separator, if it exists
   if(info.startseparator!=NULL)
      output->characters(info.startseparator,info.startseparatorlen);

   // Each decompressor decompresses its own piece of text
   // from the corresponding containers
   while(item!=NULL)
   {
      item->useruncompressor->UncompressItem(cont,dataptr,output);

      if(item->afterseparator!=NULL)
         output->characters(item->afterseparator,item->afterseparatorlen);

      cont+=item->useruncompressor->GetUserContNum();
      dataptr+=item->useruncompressor->GetUserDataSize();

      item=item->next;
   }
}

void DivSepUncompressor::FinishUncompress(UncompressContainer *cont,char *dataptr)
   // Finishes the decompression
{
   DivCompressorItem *item=info.subcompressors;

   while(item!=NULL)
   {
      item->useruncompressor->FinishUncompress(cont,dataptr);

      cont+=item->useruncompressor->GetUserContNum();
      dataptr+=item->useruncompressor->GetUserDataSize();

      item=item->next;
   }
}

DivSepCompressorFactory::DivSepCompressorFactory(Session *s): UserCompressorFactory(s)
{}

char *DivSepCompressorFactory::GetName()         
{  
	return "seq"; 
}
char *DivSepCompressorFactory::GetDescription()  
{  
	return "Sequence compressor for strings with separators"; 
}

UserCompressor *DivSepCompressorFactory::InstantiateCompressor(char *paramstr,int len)
   // Instantiates the compressor
{
   if(paramstr==NULL) {
      throw new XMillException(XMILL_ERR_ARGUMENTS, "Sequence compressor 'seq' must have a sequence of strings and compressors as parameters");
   }

   DivSepCompressor  *divsepcompressor=new DivSepCompressor(session);

   // We initialize the compressor
   divsepcompressor->CreateSubCompressors(paramstr,len);
   divsepcompressor->ComputeProperties();
   return divsepcompressor;
}

UserUncompressor *DivSepCompressorFactory::InstantiateUncompressor(char *paramstr,int len)
   // Instantiates the decompressor
{
   DivSepUncompressor  *divsepuncompressor=new DivSepUncompressor(session);

   // Intializes the decompressor
   divsepuncompressor->CreateSubCompressors(paramstr,len);
   divsepuncompressor->ComputeProperties();
   return divsepuncompressor;
}
