using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using LinqToLisp;
using Autodesk.AutoCAD.Geometry;
namespace LinqToLispExamples
{
/// <summary>
///
/// The Implode() extension method:
///
/// Since an array of TypedValue[] received from LISP
/// representing a complex list (e.g., a list with nested
/// sub-lists) is essentially 'exploded' or 'flattened',
/// the Implode() extension method performs the inverse,
/// converting nested lists into IEnumerables that will
/// allow a complex list to be dealt with in a recusive
/// manner, similar to how they are dealt with in LISP.
///
/// The IEnumerable returned by this method enumerates
/// objects that are either TypedValues (e.g. 'atoms'),
/// or objects that implement IEnumerable (nested lists)
/// that themselves, enumerate a sequence of TypedValues
/// or IEnumerables representing more-deeply nested lists.
///
/// Elements from the source TypedValue[] array that have
/// the follwing 'list-delimiter' TypeCodes never appear
/// in the sequence enumerated by this type, as all nested
/// lists are 'imploded' into IEnumerables:
///
/// LispDataType.BeginList
/// LispDataType.EndList
/// LispDataType.DottedPair
///
/// Nil and empty lists:
///
/// This class does not convert 'NIL' (e.g., an empty
/// list) into empty IEnumerables, so one should be
/// wary of that when expecting a sublist to appear
/// at a certain point in the sequence. If a list is
/// either empty, or the element is explicitly nil, a
/// TypedValue with the TypeCode LispDataType.Nil will
/// appear in the sequence.
///
/// Entity data association lists:
///
/// AutoCAD does not 'explode' or flatten association lists
/// like those returned by the LISP (entget) function, and
/// instead returns a simple array of TypedValues, where the
/// TypeCode of each element is the numeric value of each
/// sublist's first element in the LISP representation, and
/// the Value of each TypedValue is the ('cdr') of each
/// sublist.
///
/// For that reason, 'imploding' an entity data list yields
/// an IEnumerable that will enumerate the original source
/// TypedValue[] array elements.
///
/// The Dump() function in the included sample code serves
/// as an exmple of how complex, 'exploded' lists passed
/// in a managed LispFunction's ResultBuffer argument can
/// be dealt with in a recursive, or more 'lisp-ish' way.
///
/// The Implode() method and related helper classes perform
/// this task without copying or duplicating the source data,
/// making them efficient with larger amounts of data.
/// Explode(): The functional inverse of Implode() (almost)
///
/// This method can take the IEnumerable returned by a call to
/// Implode(), and return an IEnumerable<TypedValue> containing
/// the original sequence that was passed to Implode(), making
/// it the functional inverse (almost, see below).
///
/// This method can be used to build a ResultBuffer that can be
/// returned back to LISP by a called LispFunction, containing an
/// arbitrarily-complex list. The method effectively 'flattens'
/// all IEnumerbles contained in the argument, into a simple array
/// of TypedValues that produce the equivalent LISP list.
///
/// This method will flatten any list composed of IEnumerables
/// regardless of complexity or level of nesting, simplifying
/// construction of list results that are to be passed back to
/// calling LISP code.
///
/// Caveats: This method is not the exact functional inverse of
/// Implode() in one regards, which is that dotted lists are not
/// reconstituted as such. In other words, given the result of
/// passing the array of TypedValues produced by the following
/// LISP list to Implode():
///
/// ((1 . "one") (2 . "two") (3 . "three"))
///
/// And then passing that result to Explode() will, when passed
/// back to a LISP caller, yield this:
///
/// ((1 "one") (2 "two") (3 "three"))
///
/// Following the same convention used by Implode(), this method
/// will convert an empty IEnumerable (e.g., one with no elements)
/// to NIL (the empty list) in the result returned back to LISP.
///
/// The IEnumerable argument to Explode() must consist entirely of
/// TypedValue instances or instances of IEnumerable whose elements
/// are themselves TypedValue instances, recursively.
///
/// A simple example:
///
/// ArrayList list1 = new ArrayList();
/// list1.Add( new TypedValue( (short) LispDataType.Text, "a" ));
/// list1.Add( new TypedValue( (short) LispDataType.Text, "b" ));
/// list1.Add( new TypedValue( (short) LispDataType.Text, "c" ));
///
/// ArrayList list2 = new ArrayList();
/// list2.Add( new TypedValue( (short) LispDataType.Int16, 3 ));
/// list2.Add( new TypedValue( (short) LispDataType.Int16, 4 ));
/// list2.Add( list1 );
/// list2.Add( new TypedValue( (short) LispDataType.Int16, 5 ));
/// list2.Add( new TypedValue( (short) LispDataType.Int16, 6 ));
///
/// ArrayList list3 = new ArrayList();
/// list3.Add( new TypedValue( (short) LispDataType.Int16, 1 ));
/// list3.Add( new TypedValue( (short) LispDataType.Int16, 2 ));
/// list3.Add( list2 );
/// list3.Add( new TypedValue( (short) LispDataType.Int16, 7 ));
/// list3.Add( new TypedValue( (short) LispDataType.Int16, 8 ));
///
/// TypedValue[] array = list3.Explode().ToArray();
///
/// ResultBuffer result = new ResultBuffer( array );
///
/// When the above ResultBuffer 'result' is returned by a LispFunction
/// to calling LISP code, it yields the following LISP list:
///
/// (1 2 (3 4 ("a" "b" "c") 5 6) 7 8)
///
/// Notice that the complex list that is returned to LISP was
/// constructed without having to add TypedValue elements with
/// the ListDataType.BeginList and ListDataType.EndList list-
/// delimiting TypeCodes.
/// test code
public static class LinqToLispTest
{
/////////////////////////////////////////////////////////////////////
///
/// The (implode-test) lisp function:
///
/// Accepts 0 or more arguments of any type,
/// including lists, and dumps the 'imploded'
/// arguments to the console.
///
/// A few examples:
///
/// Command: (implode-test 100 200 '("how" "now" (2) "brown" (3 4 nil 5 6) "cow") 300 400)
///
/// [0]: Int16: 100
/// [1]: Int16: 200
/// [2]: (IEnumerable):
/// [0]: Text: "how"
/// [1]: Text: "now"
/// [2]: (IEnumerable):
/// [0]: Int16: 2
/// [3]: Text: "brown"
/// [4]: (IEnumerable):
/// [0]: Int16: 3
/// [1]: Int16: 4
/// [2]: Nil: (null)
/// [3]: Int16: 5
/// [4]: Int16: 6
/// [5]: Text: "cow"
/// [3]: Int16: 300
/// [4]: Int16: 400
///
/// Command:
///
/// /// NOTE: Support for round-tripping 'dotted pairs' is a major "TODO"
/// /// that has yet to be done.
///
/// Command: (implode-test '(("oranges" . 25) ("apples" . 40) ("pears". 32)))
///
/// [0]: (IEnumerable):
/// [0]: (IEnumerable):
/// [0]: Text: "oranges"
/// [1]: Int16: 25
/// [1]: (IEnumerable):
/// [0]: Text: "apples"
/// [1]: Int16: 40
/// [2]: (IEnumerable):
/// [0]: Text: "pears"
/// [1]: Int16: 32
///
/// Command: (setq somelist '("a" "b" "c" (100 200 300 400) "d" "e" "f"))
/// ("a" "b" "c" (100 200 300 400) "d" "e" "f")
///
/// Command: (apply 'implode-test somelist)
///
/// [0]: Text: "a"
/// [1]: Text: "b"
/// [2]: Text: "c"
/// [3]: (IEnumerable):
/// [0]: Int16: 100
/// [1]: Int16: 200
/// [2]: Int16: 300
/// [3]: Int16: 400
/// [4]: Text: "d"
/// [5]: Text: "e"
/// [6]: Text: "f"
///
///
/// Command: (implode-test (entget (entnext)) (entget (entlast)))
///
/// [0]: (IEnumerable):
/// [0]: -1: (8796082559792)
/// [1]: 0: "CIRCLE"
/// [2]: 330: (8796082551280)
/// [3]: 5: "1AB"
/// [4]: 100: "AcDbEntity"
/// [5]: 67: 0
/// [6]: 410: "Model"
/// [7]: 8: "0"
/// [8]: 100: "AcDbCircle"
/// [9]: 10: (0,0,0)
/// [10]: 40: 1
/// [11]: 210: (0,0,1)
/// [1]: (IEnumerable):
/// [0]: -1: (8796082559808)
/// [1]: 0: "LINE"
/// [2]: 330: (8796082551280)
/// [3]: 5: "1AC"
/// [4]: 100: "AcDbEntity"
/// [5]: 67: 0
/// [6]: 410: "Model"
/// [7]: 8: "0"
/// [8]: 100: "AcDbLine"
/// [9]: 10: (0,0,0)
/// [10]: 11: (1,0,0)
/// [11]: 210: (0,0,1)
///
/// Command:
///
[LispFunction( "implode-test" )]
public static void ImplodeTest( ResultBuffer args )
{
if( args != null )
args.Implode().Dump();
Utils.WriteLine( "\n\b\n" );
}
/// Round-tripping test
// Round-trip a list argument ( Explode( Implode(...) )
[LispFunction( "implode-explode-test" )]
public static ResultBuffer ImplodeExplodeTest( ResultBuffer args )
{
if( args != null )
{
return new ResultBuffer
( args
.Implode().Explode().ToArray() ); }
return null;
}
/// Round-trip a list argument ( Explode( Implode(...) )
///
/// With the exception of lists containing dotted lists,
/// this will return its argument (after transformation
/// to the managed LIST representation and then back to
/// the ADS/RESBUF representation).
[LispFunction( "implode-explode-roundtrip-test" )]
public static ResultBuffer ImplodeExplodeRoundTrip( ResultBuffer args )
{
if( args != null )
{
var list = args.Implode();
list.Dump();
Utils.WriteLine( "\n\b\n" );
return new ResultBuffer
( list
.Explode().ToArray() ); }
return null;
}
/// Returns the follwing back to LISP:
///
/// (1 nil 2 ("foo" "bar" (0.0 0.0 0.0) "baz") 3 T 4 5)
///
/// Note that the two bool arguments (true and false)
/// are projected to T and NIL respectively in the
/// resulting list:
///
[LispFunction( "listbuilder-return-list" )]
public static object ListBuilderTestReturnList( ResultBuffer args )
{
return ListBuilder.List(
1,
false, // false => NIL
2,
ListBuilder.List( // nested sublist
"foo",
"bar",
Point3d.Origin,
"baz"
),
3,
true, // true => 'T
4,
5
);
}
[LispFunction( "listbuilder-return-atom" )]
public static object ListBuilderTestReturnAtom( ResultBuffer args )
{
// ListBuilder creates TypedValues for any
// supported type without all the gibberish:
TypedValue tv = ListBuilder.ToTypedValue( 99 );
// returns an atom back to LISP:
return ListBuilder.RetVal( tv );
}
/// Construct and return an association list:
///
/// Command: (alist-test)
/// returns -> ((1 . "one") (2 . "two") (3 . "three"))
[LispFunction( "alist-test" )]
public static object AListTest(ResultBuffer args)
{
return ListBuilder.List(
ListBuilder.Cons( 1, "one" ),
ListBuilder.Cons( 2, "two" ),
ListBuilder.Cons( 3, "three" ) );
}
/// <summary>
/// Same as (alist-test), except the source
/// is a Dictionary<int,string>:
[LispFunction( "alist-test2" )]
public static object AListTest2( ResultBuffer args )
{
Dictionary
<int,
string> dict
= new Dictionary
<int,
string>(); dict[1] = "one";
dict[2] = "two";
dict[3] = "three";
// construct and return association list manually using Linq
// (see below for the more-automated, 'easy button' approach):
return ListBuilder.List( dict.Select( p => ListBuilder.Cons( p.Key, p.Value ) ) );
}
/// <summary>
/// Returns the same result as (alist-test2), but using
/// the ToResultBuffer() extension method:
[LispFunction( "alist-test3" )]
public static object AListTest3( ResultBuffer args )
{
Dictionary
<int,
string> dict
= new Dictionary
<int,
string>(); dict[1] = "one";
dict[2] = "two";
dict[3] = "three";
/// Returning the above dictionary as an association list
/// of the form ((<key> . <value>)...) is as simple as this:
return dict.ToResultBuffer();
}
/// Construct and return a list containing a nested association list:
///
/// Command: (nested-alist-test)
/// returns -> ("foo" ((1 . "one") (2 . "two") (3 . "three")) "bar" "baz")
[LispFunction( "nested-alist-test" )]
public static object NestedAListTest( ResultBuffer args )
{
return ListBuilder.List(
"foo",
ListBuilder.List(
ListBuilder.Cons( 1, "one" ),
ListBuilder.Cons( 2, "two" ),
ListBuilder.Cons( 3, "three" )
),
"bar",
"baz"
);
}
}
}