
BeginPackage["Examples`OptionUtilities`"]

OptionList::usage =
"OptionList[expr] returns a list of all valid options found in expr."

OptionSelect::usage =
"OptionSelect[input, target] replaces options found in target with
any corresponding options found in input."

OptionExtract::usage =
"OptionExtract[args, known] returns a list {parameters, options} unless
unknown options are found, in which case the returned value is $Failed.
args is scanned from the last element to the last non-option argument,
and any replacement rules found are substituted for the corresponding
rules in known.  Remaining args are returned as parameters."

Begin["`Private`"]

(* ==== OptionList ==================================================== *)

OptionList[expr_] := Flatten[Options[expr]]

(* ==== OptionSelect ================================================== *)

OptionSelect[input_, target_] :=
    CompoundExpression[
        targetlist = OptionList[target],
        iOptionSelect[input],
        targetlist
    ]

iOptionSelect[opts_List] := Map[iOptionSelect[#] &, opts]

iOptionSelect[rule_] :=
    Block[{name, n},
        name = First[rule];
        For[n = 1, n <= Length[targetlist], n++,
            If[name === targetlist[[n, 1]],
                targetlist[[n]] = rule;
                Break[]
            ]
        ]
    ] /; Head[rule] === Rule || Head[rule] === RuleDelayed

(* ==== OptionExtract ================================================= *)

OptionExtract[args_List, known_] :=
    Block[{n},
        validlist = OptionList[known];
        n = Length[args];
        While[n > 0,
            If[OptionQ[args[[n]]],
                If[InvalidOption[args[[n]]], Return[$Failed]],
            (* else, break at the last non-option. *)
                Break[]
            ];
            n--
        ];
        (* at this point, all options have been checked. *)
        Append[Take[args, n], validlist]
    ]

InvalidOption[opt_List] :=
    Block[{i},
        For[i = 1, i <= Length[opt], i++,
            If[InvalidOption[opt[[i]]], Return[True]]
        ];
        False
    ]

InvalidOption[opt_] :=
    Block[{i, name},
        name = First[opt];
        For[i = 1, i <= Length[validlist], i++,
            If[name === validlist[[i, 1]],
                validlist[[i]] = opt;
                Return[False]
            ]
        ];
        Message[OptionExtract::unkopt, opt];
        Return[True]
    ] /; Head[opt] === Rule || Head[opt] === RuleDelayed

InvalidOption[opt_] := (Message[OptionExtract::nonrep, opt]; True)

OptionExtract::unkopt = "Option `1` not recognized as a valid option."

OptionExtract::nonrep = "Option specification `1` was expected to be
a replacement rule or list of replacement rules."

(* ==== End =========================================================== *)

End[]

EndPackage[]

