Saturday, November 23

Testing private methods via reflection


You are not supposed to test the private methods of a class, but it is occasionally useful. Microsoft used to have a private accessor type but has since deprecated it. This is the way I came up with: Put a pulic wrapper around the methods, and use reflection to call them. Not original, but it works ok.

The reflection class has one main method: RunMethod. I added (ok, started with) a ListMethod as a helper.


namespace UnitTests.Accessors
{
    using System;
    using System.Reflection;

    /// <summary>
    /// Accessor helper, uses reflection to call private methods of a class
    /// </summary>
    class AccessorReflection
    {
        /// <summary>
        /// The BindingFlags flags used to access the class methods
        /// BindingFlags.NonPublic will not return any results by itself. 
        /// As it turns out, combining it with BindingFlags.Instance does the trick.
        /// </summary>
        private const BindingFlags accessFlags =
            BindingFlags.Instance | BindingFlags.Public |
            BindingFlags.NonPublic | BindingFlags.Static;

        /// <summary>
        /// Runs the method of the class type.
        /// </summary>
        /// <param name="objectType">The class type.</param>
        /// <param name="methodName">The method to run as a string.</param>
        /// <param name="objectInstance">The object instance.</param>
        /// <param name="objectParameters">The object method parameters.</param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentException">There is no method ' +
        ///                      strMethod + ' for type ' + t.ToString() + '.</exception>
        public static object RunMethod(Type objectType, string methodName,
            object objectInstance, object[] objectParameters)
        {
            if (String.IsNullOrEmpty(methodName))
            {
                throw new
                    ArgumentException("Cannot invoke null or empty method for type " + objectType.ToString() + "'.");
            }
            MethodInfo m = objectType.GetMethod(methodName, AccessorReflection.accessFlags);
            if (m == null)
            {
                // Method does not exist
                throw new ArgumentException("There is no method '" +
                    methodName + "' for type '" + objectType.ToString() + "'.");
            }

            return m.Invoke(objectInstance, objectParameters);
        }

        /// <summary>
        /// Lists the methods of the type. Pretty much for debugging
        /// </summary>
        /// <param name="t">The class type.</param>
        public void ListMethods(Type t)
        {
            Console.WriteLine("***** Methods *****");
            MethodInfo[] methodInfo = t.GetMethods(AccessorReflection.accessFlags);
            // sort methods by name
            Array.Sort(methodInfo,
                    delegate(MethodInfo methodInfo1, MethodInfo methodInfo2)
                    { return methodInfo1.Name.CompareTo(methodInfo2.Name); });
            foreach (MethodInfo methodEntry in methodInfo)
            {
                // Get return type.
                string retVal = methodEntry.ReturnType.FullName;
                string paramInfo = "( ";

                // Get params.
                foreach (ParameterInfo parameterInfo in methodEntry.GetParameters())
                {
                    paramInfo += string.Format("{0} {1} ", parameterInfo.ParameterType, parameterInfo.Name);
                }
                paramInfo += " )";

                // Now display the basic method sig.
                Console.WriteLine("->{0} {1} {2}", retVal, methodEntry.Name, paramInfo);
            }
            Console.WriteLine();
        }

    }
}


I also added a base abstract class to call it:

namespace UnitTests.Accessors
{
    using System;

    ///
    /// Base class to access private methods for testing.
    ///
    /// Object under test
    class AccessorBase
    {
        ///
        /// Holds the reference to the object under test
        ///
        private T objectInstance;
       
        public AccessorBase(T objectInstance)
        {
            this.objectInstance = objectInstance;
        }

        ///
        /// Executes the private method of the instance
        ///
        /// Method to execute as a string
        /// Parameters for the method
        /// The result of the method execution
        public object RunMethod(string methodName, Object[] parameters)
        {
            return AccessorReflection.RunMethod(objectInstance.GetType(), methodName, this.objectInstance, parameters);
        }
    }
}

So now write an accessor class with the public wrappers. My example is dumb but it gets the point across.

class Mine
{
    private int count;

    public Mine(int count)
    {
        this.count = count;
    }

    public void publicMethodNoArgs()
    {
        this.privateMethodNoArgs();
    }

    private void privateMethodNoArgs()
    {
        int joy = 1;
        for (int x = 1; x < 5; x++)
        {
            joy += x;
        }
    }

    private void privateMethodIntArg(int joy)
    {
        Console.WriteLine("Joy before is {0}", joy);
        for (int x = 1; x < 5; x++)
        {
            joy += x;
        }

        Console.WriteLine("Joy is now {0}", joy);
    }

    private int privateMethodStringArg(string name)
    {
        int result = 1;
        return result;
    }
}


The accessor class looks like:

class Mine_Accessor : AccessorBase<Mine_Accessor>
{
    public Mine_Accessor(Mine mineInstance): base(mineInstance)
    {
    }

    public void privateMethodIntArg(int joy)
    {
        Object[] parms = {joy};

        base.RunMethod("privateMethodIntArg", parameters);
    }
}
I like to make the public wrapper look like the private method in names and parameters. If there is a return type other than void, add a return and cast the result.

Saturday, November 2

A C doubly linked list

List.h:
#ifndef list_h
#define list_h

typedef Handle HList; /* Generic Linked List */

List ListAlloc(const int a_size);

#define ListFree(x) ListFreeX(__FILE__, __LINE__, x)

#endif

#ifdef __cplusplus
extern "C" {
#endif

void ListFreeX(const char *a_filename, int a_linenumber, const List a_hlist);
Bool ListEmpty(const List a_hlist);
int ListSize(const List a_hlist);
Bool ListFindFirst(const List a_hlist);
Bool ListFindPrior(const List a_hlist);
Bool ListFindNext(const List a_hlist);
Bool ListFindLast(const List a_hlist);
Bool ListFind(const List a_hlist,const void* a_pelement,FComp* a_pfcomp);
Bool ListInsertFirst(const List a_hlist,const void* a_pelement);
Bool ListInsertPrior(const List a_hlist,const void* a_pelement);
Bool ListInsertNext(const List a_hlist,const void* a_pelement);
Bool ListInsertLast(const List a_hlist,const void* a_pelement);
Bool ListRetrieve(const List a_hlist,void* a_pelement);
Bool ListUpdate(const List a_hlist,const void* a_pelement);
Bool ListRemove(const List a_hlist,void* a_pelement);
Bool ListMap(const List a_hlist,FMap* a_pfmap,void* a_pcontext);
Bool ListMapPos(const List a_hlist,FMap* a_pfmap,void* a_pcontext);
Bool ListSort(const List a_hlist,FComp* a_pfcomp);
List ListSortCopy(const List a_hlist,FComp* a_pfcomp);
List ListCopyX(const char *a_filename, int a_linenumber, const List a_hlist);
Bool ListAppend(List a_hlist1, const List a_hlist2 );

#ifdef __cplusplus
}
#endif

#define ListCopy(x) ListCopyX(__FILE__,__LINE__, x)
#define ListFilter(a,b,c) ListFilterX(__FILE__, __LINE__, a,b,c)

#endif

List.c:
#include
#include

#include "std.h"
#include "api.h"
#include "list.h"
#include "stdmem.h"
#include "message.h"

typedef struct  LNODE /* list node header */
{
struct LNODE*   pprior; /* prior element */
struct LNODE*   pnext; /* next element */
} lnode;

typedef struct  LIST /* list header */
{
lnode*  pfirst; /* first element */
lnode*  pcur; /* current element */
lnode*  plast; /* last element */
int     size; /* size of elements */
int count; /* # of elements */
Bool managed; /* used by higher-order structures to 'own' lists */
} list;

/***********************************************************************\
* Description *
* Allocates a list. *
* *
* Return value: *
* Failure: 0 *
* Success: Handle of list *
* *
\***********************************************************************/
const int a_size) /* size of elements */
{
register list*   l_plist; /* new list pointer */
assert(a_size>0);

l_plist=(list*)MemAlloc(poolDefault,sizeof(list)); /* allocate */
if (l_plist) /* sucess? */
{
l_plist->pfirst=0; /* empty list */
l_plist->pcur=0; /* no current */
l_plist->plast=0; /* empty list */
l_plist->size=a_size; /* record size */
l_plist->count=0; /* no elements */
l_plist->managed=False; /* not managed by default */
}
return (l_plist); /* return new list */
}

/***********************************************************************\
* Description *
* Determines whether a list is empty. *
* *
* Return value: *
* True: Empty *
* False: Not empty *
* *
\***********************************************************************/
Bool ListEmpty(const List a_hlist) /* handle of list */
{
register list*   l_plist; /* list pointer */
register Bool    l_result; /* return value */

assert(a_hlist!=0);

l_plist=(list*)a_hlist; /* convert handle */
l_result=False; /* default */
if (l_plist->pfirst==0) /* any elements? */
l_result=True; /* no */
return (l_result); /* done */
}

/***********************************************************************\
* Description *
* Determines the number of elements in the list. *
* *
* Return value: *
* # of elements *
* *
\***********************************************************************/
int ListSize(const List a_hlist)
{
register list*  l_plist; /* list pointer */
register int l_size; /* return value */

assert(a_hlist!=0);

l_plist=(list*)a_hlist; /* convert handle */
l_size=l_plist->count; /* get size */
return (l_size); /* done */
}

/***********************************************************************\
* Description *
* Marks the first element as the current. *
* *
* Return value: *
* True: Success *
* False: Failure *
* *
\***********************************************************************/
Bool ListFindFirst(const List a_hlist) /* handle of list */
{
register list*   l_plist; /* list pointer */
register lnode*  l_pnode; /* node pointer */
register Bool    l_result; /* return value */

assert(a_hlist!=0);

l_plist=(list*)a_hlist; /* convert handle */
l_pnode=l_plist->pfirst; /* get first */
l_result=False; /* default */
if (l_pnode) /* node exists? */
{
l_plist->pcur=l_pnode; /* mark as current */
l_result=True; /* success */
}
return (l_result); /* done */
}

/***********************************************************************\
* Description *
* Marks the previous element as the current. *
* *
* Return value: *
* True: Success *
* False: Failure *
* *
\***********************************************************************/
Bool ListFindPrior(const List a_hlist) /* handle of list */
{
register list*   l_plist; /* list pointer */
register lnode*  l_pnode; /* node pointer */
register Bool    l_result; /* return value */

assert(a_hlist!=0);

l_plist=(list*)a_hlist; /* convert handle */
l_pnode=l_plist->pcur; /* get current */
l_result=False; /* default */
if (l_pnode) /* node exists? */
{
l_pnode=l_pnode->pprior; /* get previous */
if (l_pnode) /* node exists? */
{
l_plist->pcur=l_pnode; /* mark as current */
l_result=True; /* success */
}
}
return (l_result); /* done */
}

/***********************************************************************\
* Description *
* Marks the following element as the current. *
* *
* Return value: *
* True: Success *
* False: Failure *
* *
\***********************************************************************/
Bool ListFindNext(const List a_hlist) /* handle of list */
{
register list*   l_plist; /* list pointer */
register lnode*  l_pnode; /* node pointer */
register Bool    l_result; /* return value */

assert(a_hlist!=0);

l_plist=(list*)a_hlist; /* convert handle */
l_pnode=l_plist->pcur; /* get current */
l_result=False; /* default */
if (l_pnode) /* node exists? */
{
l_pnode=l_pnode->pnext; /* get following */
if (l_pnode) /* node exists? */
{
l_plist->pcur=l_pnode; /* mark as current */
l_result=True; /* success */
}
}
return (l_result); /* done */
}

/***********************************************************************\
* Description *
* Marks the last element as the current. *
* *
* Return value: *
* True: Success *
* False: Failure *
* *
\***********************************************************************/
Bool ListFindLast(const List a_hlist) /* handle of list */
{
register list*   l_plist; /* list pointer */
register lnode*  l_pnode; /* node pointer */
register Bool    l_result; /* return value */

assert(a_hlist!=0);

l_plist=(list*)a_hlist; /* convert handle */
l_pnode=l_plist->plast; /* get last */
l_result=False; /* default */
if (l_pnode) /* node exists? */
{
l_plist->pcur=l_pnode; /* mark as current */
l_result=True; /* success */
}
return (l_result); /* done */
}

/***********************************************************************\
* Description *
* Searches the list for a specific element. *
* *
* Return value: *
* True: Found *
* False: Not found *
* *
\***********************************************************************/
Boo) ListFind(const List a_hlist, /* handle of list */
const void* a_pelement, /* element to find */
FComp* a_pfcomp) /* compare function */
{
register list*   l_plist; /* list pointer */
register lnode*  l_pnode; /* node pointer */
register Bool    l_result; /* return value */

assert(a_hlist!=0);
assert(a_pelement!=0);

l_plist=(list*)a_hlist; /* convert handle */
l_pnode=l_plist->pfirst; /* start with first */
l_result=False; /* default */
while (l_pnode) /* more nodes? */
{
if (a_pfcomp) /* comp function used? */
{
if (a_pfcomp(a_pelement,l_pnode+1)==0) /* equeal? */
l_result=True; /* success */
}
else
{
if (MemCompare(a_pelement,l_pnode+1,l_plist->size)==0) 
l_result=True; /* success */
}
if (l_result) /* success? */
{
l_plist->pcur=l_pnode; /* mark as current */
break; /* done */
}
else l_pnode=l_pnode->pnext; /* next node */
}
return (l_result); /* done */
}

/***********************************************************************\
* Description *
* Inserts an element at the beginning of the list. *
* *
* Return value: *
* True: Success *
* False: Failure *
* *
\***********************************************************************/
Bool ListInsertFirst(
const List a_hlist, /* handle of list */
const void* a_pelement) /* element to insert */
{
register list*   l_plist; /* list pointer */
register int     l_size; /* element size */
register lnode*  l_pnode; /* node pointer */
register Bool    l_result; /* return value */

assert(a_hlist!=0);
assert(a_pelement!=0);

l_plist=(list*)a_hlist; /* convert handle */
/* debugger stop here?  you tried to manipulate a managed list. */
assert(l_plist->managed==0);

l_size=l_plist->size; /* get size */
l_pnode=(lnode*)MemAlloc(poolDefault,sizeof(lnode)+l_size); /* allocate */
l_result=False; /* default */
if (l_pnode) /* success? */
{
l_pnode->pprior=0; /* no prior */
l_pnode->pnext=l_plist->pfirst; /* chain */
if (l_plist->pfirst) /* first exists? */
l_plist->pfirst->pprior=l_pnode; /* chain */
else
l_plist->plast=l_pnode; /* mark as last */
l_plist->pfirst=l_pnode; /* mark as first */
l_plist->pcur=l_pnode; /* mark as current */
(l_plist->count)++; /* one more */
MemCopy(l_pnode+1,a_pelement,l_size); /* copy */
l_result=True; /* success */
}
return (l_result); /* done */
}

/***********************************************************************\
* Description *
* Inserts an element before the current in the list. *
* *
* Return value: *
* True: Success *
* False: Failure *
* *
\***********************************************************************/
Bool ListInsertPrior(
const List a_hlist, /* handle of list */
const void* a_pelement) /* element to insert */
{
register list*   l_plist; /* list pointer */
register int     l_size; /* element size */
register lnode*  l_pnode; /* node pointer */
register Bool    l_result; /* return value */

assert(a_hlist!=0);
assert(a_pelement!=0);

l_plist=(list*)a_hlist; /* convert handle */
/* debugger stop here?  you tried to mainpulate a managed list. */
assert(l_plist->managed==0);

if (l_plist->pcur==l_plist->pfirst) /* current is first? */
l_result=ListInsertFirst(a_hlist,a_pelement); /* insert first */
else
{
l_size=l_plist->size; /* get size */
l_pnode=(lnode*)MemAlloc(poolDefault,sizeof(lnode)+l_size); /* allocate */
l_result=False; /* default */
if (l_pnode) /* success? */
{
l_pnode->pprior=l_plist->pcur->pprior; /* chain */
l_pnode->pnext=l_plist->pcur; /* chain */
l_pnode->pprior->pnext=l_pnode; /* chain prior */
l_pnode->pnext->pprior=l_pnode; /* chain next */
l_plist->pcur=l_pnode; /* mark as current */
(l_plist->count)++; /* one more */
MemCopy(l_pnode+1,a_pelement,l_size); /* copy */
l_result=True; /* success */
}
}
return (l_result); /* done */
}

/***********************************************************************\
* Description *
* Inserts an element after the current in the list. *
* *
* Return value: *
* True: Success *
* False: Failure *
* *
\***********************************************************************/
Bool ListInsertNext(
const List a_hlist, /* handle of list */
const void* a_pelement) /* element to insert */
{
register list*   l_plist; /* list pointer */
register int     l_size; /* element size */
register lnode*  l_pnode; /* node pointer */
register Bool    l_result; /* return value */

assert(a_hlist!=0);
assert(a_pelement!=0);

l_plist=(list*)a_hlist; /* convert handle */
/* debugger stop here?  you tried to manipulate a managed list. */
assert(l_plist->managed==0);

if (l_plist->pcur==l_plist->plast) /* current is last? */
l_result=ListInsertLast(a_hlist,a_pelement); /* insert last */
else
{
l_size=l_plist->size; /* get size */
l_pnode=(lnode*)MemAlloc(poolDefault,sizeof(lnode)+l_size); /* allocate */
l_result=False; /* default */
if (l_pnode) /* success? */
{
l_pnode->pprior=l_plist->pcur; /* chain */
l_pnode->pnext=l_plist->pcur->pnext; /* chain */
l_pnode->pprior->pnext=l_pnode; /* chain prior */
l_pnode->pnext->pprior=l_pnode; /* chain next */
l_plist->pcur=l_pnode; /* mark as current */
(l_plist->count)++; /* one more */
MemCopy(l_pnode+1,a_pelement,l_size); /* copy */
l_result=True; /* success */
}
}
return (l_result); /* done */
}

/***********************************************************************\
* Description *
* Inserts an element at the end of the list. *
* *
* Return value: *
* True: Success *
* False: Failure *
* *
\***********************************************************************/
Bool ListInsertLast(
const List a_hlist, /* handle of list */
const void* a_pelement) /* element to insert */
{
register list*   l_plist; /* list pointer */
register int     l_size; /* element size */
register lnode*  l_pnode; /* node pointer */
register Bool    l_result; /* return value */

assert(a_hlist!=0);
assert(a_pelement!=0);
l_plist=(list*)a_hlist; /* convert handle */
/* debugger stop here?  you tried to manipulate a managed list. */
assert(l_plist->managed==0);

l_size=l_plist->size; /* get size */
l_pnode=(lnode*)MemAlloc(poolDefault,sizeof(lnode)+l_size); /* allocate */
l_result=False; /* default */
if (l_pnode) /* success? */
{
l_pnode->pprior=l_plist->plast; /* chain */
l_pnode->pnext=0; /* no next */
if (l_plist->plast) /* last exists? */
l_plist->plast->pnext=l_pnode; /* chain */
else
l_plist->pfirst=l_pnode; /* mark as first */
l_plist->plast=l_pnode; /* mark as last */
l_plist->pcur=l_pnode; /* mark as current */
(l_plist->count)++; /* one more */
MemCopy(l_pnode+1,a_pelement,l_size); /* copy */
l_result=True; /* success */
}
return (l_result); /* done */
}

/***********************************************************************\
* Description *
* Retrieves the current element of the list. *
* *
* Return value: *
* True: Success *
* False: Failure *
* *
\***********************************************************************/
Bool ListRetrieve(
const List a_hlist, /* handle of list */
void* a_pelement) /* element pointer */
{
register list*   l_plist; /* list pointer */
register lnode*  l_pnode; /* node pointer */
register Bool    l_result; /* return value */

assert(a_hlist!=0);
assert(a_pelement!=0);

l_plist=(list*)a_hlist; /* convert handle */
l_pnode=l_plist->pcur; /* get current */
l_result=False; /* default */
if (l_pnode) /* node exists? */
{
MemCopy(a_pelement,l_pnode+1,l_plist->size); /* copy */
l_result=True; /* success */
}
return (l_result); /* done */
}

/***********************************************************************\
* Description *
* Updates the current element of the list. *
* *
* Return value: *
* True: Success *
* False: Failure *
* *
\***********************************************************************/
Bool ListUpdate(
const List a_hlist, /* handle of list */
const void* a_pelement) /* new element */
{
register list*   l_plist; /* list pointer */
register lnode*  l_pnode; /* node pointer */
register Bool    l_result; /* return value */

assert(a_hlist!=0);
assert(a_pelement!=0);

l_plist=(list*)a_hlist; /* convert handle */
/* debugger stop here?  you tried to manipulate a managed list. */
assert(l_plist->managed==0);

l_pnode=l_plist->pcur; /* get current */
l_result=False; /* default */
if (l_pnode) /* node exists? */
{
MemCopy(l_pnode+1,a_pelement,l_plist->size); /* copy */
l_result=True; /* success */
}
return (l_result); /* done */
}

/***********************************************************************\
* Description *
* Removes the current element of the list. *
* *
* Return value: *
* True: Success *
* False: Failure *
* *
\***********************************************************************/
Bool ListRemove(
const List a_hlist, /* handle of list */
void* a_pelement) /* element pointer */
{
register list*   l_plist; /* list pointer */
register lnode*  l_pnode; /* node pointer */
register Bool    l_result; /* return value */

assert(a_hlist!=0);

l_plist=(list*)a_hlist; /* convert pointer */
/* debugger stop here?  you tried to manipulate a managed list. */
assert(l_plist->managed==0);

l_pnode=l_plist->pcur; /* get current */
l_result=False; /* default */
if (l_pnode) /* node exists? */
{
if (l_pnode==l_plist->pfirst) /* is first? */
l_plist->pfirst=l_pnode->pnext; /* move first */
else
l_pnode->pprior->pnext=l_pnode->pnext; /* unchain */
if (l_pnode==l_plist->plast) /* is last? */
l_plist->plast=l_pnode->pprior; /* move last */
else
l_pnode->pnext->pprior=l_pnode->pprior; /* unchain */
l_plist->pcur=l_plist->pfirst; /* first is new current */
(l_plist->count)--; /* one less */
if (a_pelement) /* copy? */
MemCopy(a_pelement,l_pnode+1,l_plist->size); /* yes */
MemFree(l_pnode); /* free element */
l_result=True; /* success */
}
return (l_result); /* done */
}

/***********************************************************************\
* Description *
* Maps a function onto each element of a list. *
* *
* Return value: *
* True: Success *
* False: Failure *
* *
\***********************************************************************/
Bool ListMap(
const List a_hlist, /* handle of list */
FMap* a_pfmap, /* map function */
void* a_pcontext) /* context specific */
{
register list*   l_plist; /* list pointer */
register lnode*  l_pnode; /* node pointer */
register Bool    l_result; /* return value */

assert(a_hlist!=0);
assert(a_pfmap!=0);

l_plist=(list*)a_hlist; /* convert handle */
l_pnode=l_plist->pfirst; /* start at first */
l_result=True; /* default */
while (l_pnode) /* more nodex? */
{
l_result=a_pfmap(l_pnode+1,a_pcontext); /* apply function */
if (!l_result) /* success? */
break; /* no */
l_pnode=l_pnode->pnext; /* next node */
}
return (l_result); /* done */
}

/***********************************************************************\
* Description *
* Maps a function onto each element of a list. *
* Mapping begins on the current node as opposed to the first *
* node. *
* *
* Return value: *
* True: Success *
* False: Failure *
* *
\***********************************************************************/
Bool ListMapPos(const List a_hlist, FMap* a_pfmap, void* a_pcontext)
{
  register list*   l_plist; /* list pointer */
  register lnode*  l_pnode; /* node pointer */
  register Bool    l_result; /* return value */

  assert(a_hlist!=0);
  assert(a_pfmap!=0);

  l_plist=(list*)a_hlist; /* convert handle */
  l_pnode=l_plist->pcur; /* start at first */
  l_result=True; /* default */
  while (l_pnode) /* more nodex? */
    {
      l_result=a_pfmap(l_pnode+1,a_pcontext); /* apply function */
      if (!l_result) /* success? */
break; /* no */
      l_pnode=l_pnode->pnext; /* next node */
    }
  return (l_result); /* done */
}

/***********************************************************************\
* Description *
* *
* Return value: *
* *
\***********************************************************************/
Bool ListFilterX(
const char *a_filename, 
int a_linenumber,
const List a_hlist,
FFilter* a_pffilter,
void* a_pcontext
)
{
register list*   l_plist; /* list pointer */
register lnode*  l_pnode; /* node pointer */
register int l_temp; /* filter result */
register Bool l_result; /* return value */
Bool debug;

assert(a_hlist!=0);
assert(a_pffilter!=0);

l_plist=(list*)a_hlist; /* convert handle */

/* debugger stop here?  you tried to manipulate a managed list. */
if (l_plist->managed != 0)
{
MessageError ("Managed list freed manually in file %s, line %d",a_filename,a_linenumber);
assert(False);
}
assert(l_plist->managed==0);

l_pnode=l_plist->pfirst; /* start at first */
l_result=True; /* default */
while (l_pnode) /* more nodex? */
{
l_plist->pcur=l_pnode; /* set as current */
l_temp=a_pffilter(l_pnode+1,a_pcontext); /* apply function */
l_pnode=l_pnode->pnext; /* next node */
if (l_temp<0 class="Apple-tab-span" span="" style="white-space: pre;">
/* failure? */
{
l_result=False; /* yes */
break;
}
if (l_temp==0) /* success? */
{
debug=ListRemove(a_hlist,0); /* delete */
assert(debug);
(void)debug;
}
}
return (l_result); /* done */
}

static FComp* gs_pfcomp=0; /* sort pointer */

/***********************************************************************\
* Description *
* Compares two elements to each other *
* *
* Return value: *
* Negative: < *
* Zero: = *
* Positive: > *
* *
\***********************************************************************/
static int ListSortComp(
const void* a_pleft, /* left element */
const void* a_pright) /* right element */
{
lnode* l_pleft; /* node pointer */
lnode* l_pright; /* node pointer */
int l_result; /* return value */

assert(a_pleft!=0);
assert(a_pright!=0);
assert(gs_pfcomp!=0);

l_pleft=*(lnode**)a_pleft; /* get left */
l_pright=*(lnode**)a_pright; /* get right */
l_result=gs_pfcomp(l_pleft+1,l_pright+1); /* compare */
return (l_result); /* done */
}

/***********************************************************************\
* Description *
* Sorts a list in place. *
* *
* Return value: *
* True: Success *
* False: Failure *
* *
\***********************************************************************/
Bool ListSort(
const List a_hlist, /* handle of list */
FComp* a_pfcomp) /* compare function */
{
Bool l_result; /* return value */
int l_limit; /* list size */
lnode** l_ppnode; /* array pointer */
list*   l_plist; /* list pointer */
lnode* l_pnode; /* node pointer */
int l_index; /* loop counter */

assert(a_hlist!=0);
assert(a_pfcomp!=0);

l_result=True; /* default */
l_limit=ListSize(a_hlist); /* get # of elements */
if (l_limit>1) /* anything to sort? */
{
l_result=False; /* default */
l_ppnode=(lnode**)MemAlloc( /* alloc */
poolDefault,
(l_limit*sizeof(lnode*)));
if (l_ppnode!=0) /* success? */
{
l_plist=(list*)a_hlist; /* convert handle */
l_pnode=l_plist->pfirst;/* start at head */
for (l_index=0; l_index
{
l_ppnode[l_index]=l_pnode; /* store */
l_pnode=l_pnode->pnext; /* get next */
}
gs_pfcomp=a_pfcomp; /* get comp func */
qsort(
(void*)l_ppnode,
(size_t)l_limit,
sizeof(lnode*),
ListSortComp);
gs_pfcomp=0; /* reset comp func */
for (l_index=0; l_index
{
l_ppnode[l_index]->pprior= /* prior */
((l_index==0)
?0
:l_ppnode[l_index-1]);
l_ppnode[l_index]->pnext= /* next */
((l_index==(l_limit-1))
?0
:l_ppnode[l_index+1]);
}
l_plist->pfirst=l_ppnode[0]; /* get new first */
l_plist->plast=l_ppnode[l_limit-1]; /* get new last */
l_result=True; /* success */
MemFree(l_ppnode); /* free */
}
}
return (l_result); /* done */
}

/***********************************************************************\
* Description *
* Creates a sorted copy of a list. *
* *
* Return value: *
* Failure: 0 *
* Success: Handle of list *
* *
\***********************************************************************/
HList ListSortCopy(
const List a_hlist, /* handle of list */
FComp* a_pfcomp) /* compare function */
{
HList l_hlist; /* return value */

assert(a_hlist!=0);
assert(a_pfcomp!=0);

l_hlist=ListCopy(a_hlist); /* make copy */
if (l_hlist!=0) /* success? */
{
if (!ListSort(l_hlist,a_pfcomp))/* sorted? */
{
ListFree(l_hlist); /* no, free */
l_hlist=0; /* fail */
}
}
return (l_hlist); /* done */
}


/***********************************************************************\
* Description *
* Adds the element to a list. *
* *
* Return value: *
* True: Success *
* False: Failure *
* *
\***********************************************************************/
static Bool ListCopyMap(
void* a_pelement, /* element pointer */
void* a_pcontext) /* HList pointer */
{
register HList l_hlist; /* list handle */
register Bool l_result; /* return value */

assert(a_pelement!=0);
assert(a_pcontext!=0);

l_hlist=*(HList*)a_pcontext; /* reinterpret */
l_result=ListInsertLast(l_hlist,a_pelement); /* add to list */
return (l_result); /* done */
}

/***********************************************************************\
* Description *
* Creates a copy of a list. *
* *
* Return value: *
* Failure: 0 *
* Success: Handle of list *
* *
\***********************************************************************/
List ListCopyX(
const char *a_filename, 
int a_linenumber,
const HList a_hlist)
{
list*   l_plist; /* list pointer */
HList l_result; /* return value */
void* l_pcontext; /* context pointer */

assert(a_hlist!=0);

l_plist=(list*)a_hlist; /* convert handle */
l_result=ListAlloc(l_plist->size); /* create new list */
if (l_result!=0) /* success? */
{
l_pcontext=&l_result; /* pass result */
if (!ListMap(a_hlist,ListCopyMap,l_pcontext)) /* copy */
{
ListFree(l_result); /* free list */
l_result=0; /* no result */
}
}
return (l_result); /* done */
}

/***********************************************************************\
* Description *
* Appends the contents of one list to another. *
*   NOTE: ListAppend uses ListCopyMap, which creates a new copy of the data
*         at a_hlist2. So... caller needs to be sure to free the memory
*         for a_hlist2 if it's no longer needed.
* *
* Return value: *
* Failure: 0 *
* Success: Handle of list *
* *
\***********************************************************************/
Bool ListAppend(List a_hlist1, const List a_hlist2)
{
list*   l_plist1; /* list pointer */
list*   l_plist2; /* list pointer */
Bool l_result; /* return value */

assert(a_hlist1!=0);
assert(a_hlist2!=0);

l_plist1=(list*)a_hlist1; /* convert handle */
/* debugger stop here?  you tried to manipulate a managed list. */
assert(l_plist1->managed==0);
l_plist2=(list*)a_hlist2; /* convert handle */

/* Both lists should contain the same size elements */
assert( l_plist1->size == l_plist2->size );

l_result = True;
if (!ListMap(l_plist2,ListCopyMap,&l_plist1)) /* copy */
{
l_result=False; /* no result */
}
return (l_result); /* done */
}

#endif