<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://alteeve.com/w/index.php?action=history&amp;feed=atom&amp;title=Source_AN%3A%3ATools%3A%3AString</id>
	<title>Source AN::Tools::String - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://alteeve.com/w/index.php?action=history&amp;feed=atom&amp;title=Source_AN%3A%3ATools%3A%3AString"/>
	<link rel="alternate" type="text/html" href="https://alteeve.com/w/index.php?title=Source_AN::Tools::String&amp;action=history"/>
	<updated>2026-05-30T20:46:25Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.42.3</generator>
	<entry>
		<id>https://alteeve.com/w/index.php?title=Source_AN::Tools::String&amp;diff=4768&amp;oldid=prev</id>
		<title>Digimer at 03:54, 5 May 2013</title>
		<link rel="alternate" type="text/html" href="https://alteeve.com/w/index.php?title=Source_AN::Tools::String&amp;diff=4768&amp;oldid=prev"/>
		<updated>2013-05-05T03:54:13Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en-GB&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 03:54, 5 May 2013&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l1&quot;&gt;Line 1:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 1:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{mod_header}}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{mod_header}}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&#039;&#039;&#039;[https://alteeve.&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;com&lt;/del&gt;/AN/Tools/String.pm String.pm]&#039;&#039;&#039;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&#039;&#039;&#039;[https://alteeve.&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;ca&lt;/ins&gt;/AN/Tools/String.pm String.pm]&#039;&#039;&#039;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;source lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;source lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;

&lt;!-- diff cache key an_wiki-mediawiki-:diff:1.41:old-76:rev-4768:php=table --&gt;
&lt;/table&gt;</summary>
		<author><name>Digimer</name></author>
	</entry>
	<entry>
		<id>https://alteeve.com/w/index.php?title=Source_AN::Tools::String&amp;diff=76&amp;oldid=prev</id>
		<title>Digimer at 03:38, 20 September 2009</title>
		<link rel="alternate" type="text/html" href="https://alteeve.com/w/index.php?title=Source_AN::Tools::String&amp;diff=76&amp;oldid=prev"/>
		<updated>2009-09-20T03:38:41Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en-GB&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 03:38, 20 September 2009&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l2&quot;&gt;Line 2:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 2:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;#039;&amp;#039;&amp;#039;[https://alteeve.com/AN/Tools/String.pm String.pm]&amp;#039;&amp;#039;&amp;#039;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;#039;&amp;#039;&amp;#039;[https://alteeve.com/AN/Tools/String.pm String.pm]&amp;#039;&amp;#039;&amp;#039;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;----&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt; &lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;source lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;source lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;package AN::Tools::String;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;package AN::Tools::String;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;

&lt;!-- diff cache key an_wiki-mediawiki-:diff:1.41:old-73:rev-76:php=table --&gt;
&lt;/table&gt;</summary>
		<author><name>Digimer</name></author>
	</entry>
	<entry>
		<id>https://alteeve.com/w/index.php?title=Source_AN::Tools::String&amp;diff=73&amp;oldid=prev</id>
		<title>Digimer: Created page with &#039;{{mod_header}}  &#039;&#039;&#039;[https://alteeve.com/AN/Tools/String.pm String.pm]&#039;&#039;&#039; ---- &lt;source lang=&quot;perl&quot;&gt; package AN::Tools::String;  use strict; use warnings;  our $VERSION=&quot;0.1.001&quot;; m…&#039;</title>
		<link rel="alternate" type="text/html" href="https://alteeve.com/w/index.php?title=Source_AN::Tools::String&amp;diff=73&amp;oldid=prev"/>
		<updated>2009-09-20T03:17:50Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;#039;{{mod_header}}  &amp;#039;&amp;#039;&amp;#039;[https://alteeve.com/AN/Tools/String.pm String.pm]&amp;#039;&amp;#039;&amp;#039; ---- &amp;lt;source lang=&amp;quot;perl&amp;quot;&amp;gt; package AN::Tools::String;  use strict; use warnings;  our $VERSION=&amp;quot;0.1.001&amp;quot;; m…&amp;#039;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Páàjì titun&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{mod_header}}&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;[https://alteeve.com/AN/Tools/String.pm String.pm]&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;source lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
package AN::Tools::String;&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use warnings;&lt;br /&gt;
&lt;br /&gt;
our $VERSION=&amp;quot;0.1.001&amp;quot;;&lt;br /&gt;
my $THIS_FILE=&amp;quot;String.pm&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
	my $class=shift;&lt;br /&gt;
	&lt;br /&gt;
	my $self={&lt;br /&gt;
		READ		=&amp;gt;	&amp;quot;./tools.xml&amp;quot;,&lt;br /&gt;
		HASH		=&amp;gt;	{},&lt;br /&gt;
		FORCE_UTF8	=&amp;gt;	0,&lt;br /&gt;
		DEFAULT_LANG	=&amp;gt;	&amp;quot;en_CA&amp;quot;,&lt;br /&gt;
	};&lt;br /&gt;
	&lt;br /&gt;
	bless $self, $class;&lt;br /&gt;
	&lt;br /&gt;
	return ($self);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Get a handle on the AN::Tools object. I know that technically that is a&lt;br /&gt;
# sibling module, but it makes more sense in this case to think of it as a&lt;br /&gt;
# parent.&lt;br /&gt;
sub parent&lt;br /&gt;
{&lt;br /&gt;
	my $self=shift;&lt;br /&gt;
	my $parent=shift;&lt;br /&gt;
	&lt;br /&gt;
	$self-&amp;gt;{HANDLE}{TOOLS}=$parent if $parent;&lt;br /&gt;
	&lt;br /&gt;
	return ($self-&amp;gt;{HANDLE}{TOOLS});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# This forces UTF8 mode when reading a words file. This should not be used&lt;br /&gt;
# normally as the words file should already be UTF8 encoded.&lt;br /&gt;
sub force_utf8&lt;br /&gt;
{&lt;br /&gt;
	my $self=shift;&lt;br /&gt;
	my $set=defined $_[0] ? shift : undef;&lt;br /&gt;
	&lt;br /&gt;
	if (defined $set)&lt;br /&gt;
	{&lt;br /&gt;
		if (($set==0) || ($set==1))&lt;br /&gt;
		{&lt;br /&gt;
			$self-&amp;gt;{FORCE_UTF8}=$set;&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			my $an=$self-&amp;gt;parent;&lt;br /&gt;
			$an-&amp;gt;Alert-&amp;gt;error({&lt;br /&gt;
				fatal	=&amp;gt;	1,&lt;br /&gt;
				title	=&amp;gt;	&amp;quot;Invalid argument&amp;quot;,&lt;br /&gt;
				message	=&amp;gt;	&amp;quot;The invalid argument: [$set] was passed into the &amp;#039;AN::Tools::String-&amp;gt;force_utf8()&amp;#039;. Only 1 or 0 are valid arguments.&amp;quot;,&lt;br /&gt;
				code	=&amp;gt;	14,&lt;br /&gt;
				file	=&amp;gt;	&amp;quot;$THIS_FILE&amp;quot;,&lt;br /&gt;
				line	=&amp;gt;	__LINE__&lt;br /&gt;
			});&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	return ($self-&amp;gt;{FORCE_UTF8});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# This takes a word key and, optionally, a hash reference, a language and/or an&lt;br /&gt;
# variables array reference. It returns the corresponding string from the hash&lt;br /&gt;
# reference data containing the data from a &amp;#039;read_words()&amp;#039; call.&lt;br /&gt;
sub get_string&lt;br /&gt;
{&lt;br /&gt;
	my $self=shift;&lt;br /&gt;
	my $param=shift;&lt;br /&gt;
	&lt;br /&gt;
	# This just makes the code more consistent.&lt;br /&gt;
	my $an=$self-&amp;gt;parent;&lt;br /&gt;
	&lt;br /&gt;
	# Clear any prior errors as I may set one here.&lt;br /&gt;
	$an-&amp;gt;Alert-&amp;gt;_set_error;&lt;br /&gt;
	&lt;br /&gt;
	my $key;&lt;br /&gt;
	my $hash=$an-&amp;gt;data;&lt;br /&gt;
	my $vars;&lt;br /&gt;
	my $lang=$self-&amp;gt;{DEFAULT_LANG};&lt;br /&gt;
	&lt;br /&gt;
	# Now see if the user passed the values in a hash reference or&lt;br /&gt;
	# directly.&lt;br /&gt;
	if (ref($param) eq &amp;quot;HASH&amp;quot;)&lt;br /&gt;
	{&lt;br /&gt;
		# Values passed in a hash, good.&lt;br /&gt;
		$key=$param-&amp;gt;{key} if $param-&amp;gt;{key};&lt;br /&gt;
		$vars=$param-&amp;gt;{variable} if $param-&amp;gt;{variable};&lt;br /&gt;
		$hash=$param-&amp;gt;{hash} if $param-&amp;gt;{hash};&lt;br /&gt;
		$lang=$param-&amp;gt;{language} if $param-&amp;gt;{language};&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		# Values passed directly.&lt;br /&gt;
		$key=$param;&lt;br /&gt;
		$vars=$_[0] if defined $_[0];&lt;br /&gt;
		$hash=$_[1] if defined $_[1];&lt;br /&gt;
		$lang=$_[2] if defined $_[2];&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	# Make sure that &amp;#039;hash&amp;#039; is a hash reference&lt;br /&gt;
	if (ref($hash) ne &amp;quot;HASH&amp;quot;)&lt;br /&gt;
	{&lt;br /&gt;
		$an-&amp;gt;Alert-&amp;gt;error({&lt;br /&gt;
			fatal	=&amp;gt;	1,&lt;br /&gt;
			title	=&amp;gt;	&amp;quot;Invalid Argument&amp;quot;,&lt;br /&gt;
			message	=&amp;gt;	&amp;quot;The &amp;#039;AN::Tools::String&amp;#039; module&amp;#039;s &amp;#039;get_string&amp;#039; method was passed an invalid &amp;#039;hash&amp;#039; argument: [$hash]. This must be a hash reference containing data read in from an XML words file by the &amp;#039;read_words()&amp;#039; method.&amp;quot;,&lt;br /&gt;
			code	=&amp;gt;	15,&lt;br /&gt;
			file	=&amp;gt;	&amp;quot;$THIS_FILE&amp;quot;,&lt;br /&gt;
			line	=&amp;gt;	__LINE__&lt;br /&gt;
		});&lt;br /&gt;
		# Return nothing in case the user is blocking fatal&lt;br /&gt;
		# errors.&lt;br /&gt;
		return (undef);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	# Make sure that &amp;#039;vars&amp;#039; is an array reference, if set.&lt;br /&gt;
	if (($vars) &amp;amp;&amp;amp; (ref($vars) ne &amp;quot;ARRAY&amp;quot;))&lt;br /&gt;
	{&lt;br /&gt;
		$an-&amp;gt;Alert-&amp;gt;error({&lt;br /&gt;
			fatal	=&amp;gt;	1,&lt;br /&gt;
			title	=&amp;gt;	&amp;quot;Invalid Argument&amp;quot;,&lt;br /&gt;
			message	=&amp;gt;	&amp;quot;The &amp;#039;AN::Tools::String&amp;#039; module&amp;#039;s &amp;#039;get_string&amp;#039; method was passed an invalid &amp;#039;variable&amp;#039; argument: [$vars]. This must be an array reference containing elements intended to replace corresponding #!var!x!# replacement keys in the requested string.&amp;quot;,&lt;br /&gt;
			code	=&amp;gt;	16,&lt;br /&gt;
			file	=&amp;gt;	&amp;quot;$THIS_FILE&amp;quot;,&lt;br /&gt;
			line	=&amp;gt;	__LINE__&lt;br /&gt;
		});&lt;br /&gt;
		return (undef);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	# Make sure that the request language exists in the hash.&lt;br /&gt;
	if (ref($hash-&amp;gt;{words}{lang}{$lang}) ne &amp;quot;HASH&amp;quot;)&lt;br /&gt;
	{&lt;br /&gt;
		$an-&amp;gt;Alert-&amp;gt;error({&lt;br /&gt;
			fatal	=&amp;gt;	1,&lt;br /&gt;
			title	=&amp;gt;	&amp;quot;Invalid Language&amp;quot;,&lt;br /&gt;
			message	=&amp;gt;	&amp;quot;The &amp;#039;AN::Tools::String&amp;#039; module&amp;#039;s &amp;#039;get_string&amp;#039; method was passed an invalid &amp;#039;language&amp;#039; argument: [$lang]. This must match one of the languages in the words file&amp;#039;s &amp;lt;langs&amp;gt;...&amp;lt;/langs&amp;gt; block.\n&amp;quot;,&lt;br /&gt;
			code	=&amp;gt;	17,&lt;br /&gt;
			file	=&amp;gt;	&amp;quot;$THIS_FILE&amp;quot;,&lt;br /&gt;
			line	=&amp;gt;	__LINE__&lt;br /&gt;
		});&lt;br /&gt;
		return (undef);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	# Make sure that the request key is in the language hash.&lt;br /&gt;
	if (not exists $hash-&amp;gt;{words}{lang}{$lang}{key}{$key}{content})&lt;br /&gt;
	{&lt;br /&gt;
		$an-&amp;gt;Alert-&amp;gt;error({&lt;br /&gt;
			fatal	=&amp;gt;	1,&lt;br /&gt;
			title	=&amp;gt;	&amp;quot;Invalid String Key&amp;quot;,&lt;br /&gt;
			message	=&amp;gt;	&amp;quot;The &amp;#039;AN::Tools::String&amp;#039; module&amp;#039;s &amp;#039;get_string&amp;#039; method was passed the &amp;#039;key&amp;#039; argument: [$key] which was not found in the language: [$lang]. This key must be defined in one of the read in words files.\n&amp;quot;,&lt;br /&gt;
			code	=&amp;gt;	18,&lt;br /&gt;
			file	=&amp;gt;	&amp;quot;$THIS_FILE&amp;quot;,&lt;br /&gt;
			line	=&amp;gt;	__LINE__&lt;br /&gt;
		});&lt;br /&gt;
		return (undef);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	# Now pick out my actual string.&lt;br /&gt;
	my $string=$hash-&amp;gt;{words}{lang}{$lang}{key}{$key}{content};&lt;br /&gt;
	$string=~s/^\n//;&lt;br /&gt;
	&lt;br /&gt;
	# This clears off the new-line and trailing white-spaces caused by the&lt;br /&gt;
	# indenting of the &amp;#039;&amp;lt;/key&amp;gt;&amp;#039; field in the words XML file when printing&lt;br /&gt;
	# to the command line.&lt;br /&gt;
	$string=~s/\n(\s+)$//;&lt;br /&gt;
	&lt;br /&gt;
	# Substitute in any variables if needed.&lt;br /&gt;
	if ($vars)&lt;br /&gt;
	{&lt;br /&gt;
		$string=$an-&amp;gt;String-&amp;gt;_insert_vars({&lt;br /&gt;
			  string	=&amp;gt;	$string,&lt;br /&gt;
			  variable	=&amp;gt;	$vars,&lt;br /&gt;
		});&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	# Process the just-read string.&lt;br /&gt;
	$string=$an-&amp;gt;String-&amp;gt;_process({&lt;br /&gt;
		  string	=&amp;gt;	$string,&lt;br /&gt;
		  language	=&amp;gt;	$lang,&lt;br /&gt;
		  hash		=&amp;gt;	$hash,&lt;br /&gt;
	});&lt;br /&gt;
	&lt;br /&gt;
	return ($string);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# This takes a string and substitutes out the various replacement keys as&lt;br /&gt;
# needed until the string is ready for display. The only thing it doesn&amp;#039;t&lt;br /&gt;
# handle is substituting &amp;#039;#!var!x!#&amp;#039; keys into a string. For that, call the&lt;br /&gt;
# &amp;#039;get_string&amp;#039; method with it&amp;#039;s given variable array reference and store the&lt;br /&gt;
# results in a string. This is requried because there is currently no way for&lt;br /&gt;
# any of the called methods within here to know which string the variables in&lt;br /&gt;
# the array reference belong in.&lt;br /&gt;
sub _process&lt;br /&gt;
{&lt;br /&gt;
	my $self=shift;&lt;br /&gt;
	my $param=shift;&lt;br /&gt;
	&lt;br /&gt;
	my $an=$self-&amp;gt;parent;&lt;br /&gt;
	&lt;br /&gt;
	# Start looping through the passed string until all the replacement&lt;br /&gt;
	# keys are gone.&lt;br /&gt;
	my $i=0;&lt;br /&gt;
	while ( $param-&amp;gt;{string} =~ /#!(.+?)!#/ )&lt;br /&gt;
	{&lt;br /&gt;
		# Substitute &amp;#039;word&amp;#039; keys, but without &amp;#039;vars&amp;#039;. This has to be&lt;br /&gt;
		# first! &amp;#039;protect&amp;#039; will catch &amp;#039;word&amp;#039; keys, because no where&lt;br /&gt;
		# else are they allowed.&lt;br /&gt;
		$param-&amp;gt;{string}=$an-&amp;gt;String-&amp;gt;_insert_word({&lt;br /&gt;
			  string	=&amp;gt;	$param-&amp;gt;{string},&lt;br /&gt;
			  language	=&amp;gt;	$param-&amp;gt;{language},&lt;br /&gt;
			  hash		=&amp;gt;	$param-&amp;gt;{hash},&lt;br /&gt;
		});&lt;br /&gt;
		&lt;br /&gt;
		# Protect unmatchable keys.&lt;br /&gt;
		$param-&amp;gt;{string}=$an-&amp;gt;String-&amp;gt;_protect({&lt;br /&gt;
			string		=&amp;gt;	$param-&amp;gt;{string},&lt;br /&gt;
		});&lt;br /&gt;
		&lt;br /&gt;
		# Inject any &amp;#039;data&amp;#039; values.&lt;br /&gt;
		$param-&amp;gt;{string}=$an-&amp;gt;String-&amp;gt;_insert_data({&lt;br /&gt;
			string	=&amp;gt;	$param-&amp;gt;{string},&lt;br /&gt;
		});&lt;br /&gt;
		&lt;br /&gt;
		die &amp;quot;Infinite loop detected while processing the string: [$param-&amp;gt;{string}], exiting.\n&amp;quot; if $i &amp;gt; $an-&amp;gt;_error_limit;&lt;br /&gt;
		$i++;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	# Restore and unrecognized substitution values.&lt;br /&gt;
	$param-&amp;gt;{string}=$an-&amp;gt;String-&amp;gt;_restore_protected({&lt;br /&gt;
		string		=&amp;gt;	$param-&amp;gt;{string},&lt;br /&gt;
	});&lt;br /&gt;
	&lt;br /&gt;
	# Do any output mode specific formatting.&lt;br /&gt;
	$param-&amp;gt;{string}=$an-&amp;gt;String-&amp;gt;_format_mode({&lt;br /&gt;
		string		=&amp;gt;	$param-&amp;gt;{string},&lt;br /&gt;
	});&lt;br /&gt;
	&lt;br /&gt;
	return ($param-&amp;gt;{string});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Do any output mode specific formatting.&lt;br /&gt;
sub _format_mode&lt;br /&gt;
{&lt;br /&gt;
	my $self=shift;&lt;br /&gt;
	my $param=shift;&lt;br /&gt;
	&lt;br /&gt;
	my $an=$self-&amp;gt;parent;&lt;br /&gt;
	&lt;br /&gt;
	# I don&amp;#039;t think I need this now as I only wrap the string after it&amp;#039;s&lt;br /&gt;
	# been processed by &amp;#039;print_template&amp;#039;. It may have future use though.&lt;br /&gt;
	return ($param-&amp;gt;{string});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# This restores the original key format for keys that were protected by the&lt;br /&gt;
# &amp;#039;_protect&amp;#039; method.&lt;br /&gt;
sub _restore_protected&lt;br /&gt;
{&lt;br /&gt;
	my $self=shift;&lt;br /&gt;
	my $param=shift;&lt;br /&gt;
	&lt;br /&gt;
	my $an=$self-&amp;gt;parent;&lt;br /&gt;
	&lt;br /&gt;
	# Restore and unrecognized substitution values.&lt;br /&gt;
	my $i=0;&lt;br /&gt;
	while ( $param-&amp;gt;{string} =~ /!#(.+?)#!/ )&lt;br /&gt;
	{&lt;br /&gt;
		my $check=$1;&lt;br /&gt;
		$param-&amp;gt;{string}=~s/!#$check#!/#!$check!#/g;&lt;br /&gt;
		&lt;br /&gt;
		die &amp;quot;Infinite loop detected while restoring protected replacement keys in the string: [$param-&amp;gt;{string}], exiting.\n&amp;quot; if $i &amp;gt; $an-&amp;gt;_error_limit;&lt;br /&gt;
		$i++;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	return ($param-&amp;gt;{string});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# This does the actual work of substituting &amp;#039;data&amp;#039; keys.&lt;br /&gt;
sub _insert_data&lt;br /&gt;
{&lt;br /&gt;
	my $self=shift;&lt;br /&gt;
	my $param=shift;&lt;br /&gt;
	&lt;br /&gt;
	my $an=$self-&amp;gt;parent;&lt;br /&gt;
	&lt;br /&gt;
	my $i=0;&lt;br /&gt;
	while ($param-&amp;gt;{string} =~ /#!data!(.+?)!#/)&lt;br /&gt;
	{&lt;br /&gt;
		my $id=$1;&lt;br /&gt;
		if ( $id =~ /::/ )&lt;br /&gt;
		{&lt;br /&gt;
			# Multi-dimensional hash.&lt;br /&gt;
			my $value=$an-&amp;gt;_get_hash_reference({&lt;br /&gt;
				key	=&amp;gt;	$id,&lt;br /&gt;
			});&lt;br /&gt;
			if (not defined $value)&lt;br /&gt;
			{&lt;br /&gt;
				$param-&amp;gt;{string}=~s/#!data!$id!#/!!a[$id]!!/;&lt;br /&gt;
			}&lt;br /&gt;
			else&lt;br /&gt;
			{&lt;br /&gt;
				$param-&amp;gt;{string}=~s/#!data!$id!#/$value/;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			# One dimension&lt;br /&gt;
			if (not defined $an-&amp;gt;data-&amp;gt;{$id})&lt;br /&gt;
			{&lt;br /&gt;
				$param-&amp;gt;{string}=~s/#!data!$id!#/!!b[$id]!!/;&lt;br /&gt;
			}&lt;br /&gt;
			else&lt;br /&gt;
			{&lt;br /&gt;
				my $val=$an-&amp;gt;data-&amp;gt;{$id};&lt;br /&gt;
				$param-&amp;gt;{string}=~s/#!data!$id!#/$val/;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		die &amp;quot;Infinite loop detected while replacing data keys in the string: [$param-&amp;gt;{string}], exiting.\n&amp;quot; if $i &amp;gt; $an-&amp;gt;_error_limit;&lt;br /&gt;
		$i++;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	return ($param-&amp;gt;{string});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Protect unrecognized or unused replacement keys. I do this to protect strings&lt;br /&gt;
# possibly set or created by a user.&lt;br /&gt;
sub _protect&lt;br /&gt;
{&lt;br /&gt;
	my $self=shift;&lt;br /&gt;
	my $param=shift;&lt;br /&gt;
	&lt;br /&gt;
	my $an=$self-&amp;gt;parent;&lt;br /&gt;
	&lt;br /&gt;
	my $i=0;&lt;br /&gt;
	foreach my $check ( $param-&amp;gt;{string} =~ /#!(.+?)!#/ )&lt;br /&gt;
	{&lt;br /&gt;
		if (( $check !~ /^free/ ) &amp;amp;&amp;amp;&lt;br /&gt;
		( $check !~ /^replace/ ) &amp;amp;&amp;amp;&lt;br /&gt;
		( $check !~ /^data/ ) &amp;amp;&amp;amp;&lt;br /&gt;
		( $check !~ /^word/ ) &amp;amp;&amp;amp;&lt;br /&gt;
		( $check !~ /^var/ ))&lt;br /&gt;
		{&lt;br /&gt;
			# Simply invert the &amp;#039;#!...!#&amp;#039; to &amp;#039;!#...#!&amp;#039;.&lt;br /&gt;
			$param-&amp;gt;{string}=~s/#!($check)!#/!#$1#!/g;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		die &amp;quot;Infinite loop detected while protecting replacement keys in the string: [$param-&amp;gt;{string}], exiting.\n&amp;quot; if $i &amp;gt; $an-&amp;gt;_error_limit;&lt;br /&gt;
		$i++;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	return ($param-&amp;gt;{string});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# This is called to process &amp;#039;#!word!...!#&amp;#039; keys in string. It DOES NOT&lt;br /&gt;
# support substituting &amp;#039;#!var!x!#&amp;#039; keys found in imported word strings! This&lt;br /&gt;
# is meant to insert simple word strings into template files.&lt;br /&gt;
sub _insert_word&lt;br /&gt;
{&lt;br /&gt;
	my $self=shift;&lt;br /&gt;
	my $param=shift;&lt;br /&gt;
	&lt;br /&gt;
	my $an=$self-&amp;gt;parent;&lt;br /&gt;
	&lt;br /&gt;
	# Loop through the string until all &amp;#039;#!word!...!#&amp;#039; keys are gone.&lt;br /&gt;
	my $i=0;&lt;br /&gt;
	while ($param-&amp;gt;{string} =~ /#!word!(.+?)!#/)&lt;br /&gt;
	{&lt;br /&gt;
		my $key=$1;&lt;br /&gt;
		my $say_word=$an-&amp;gt;String-&amp;gt;get_string({&lt;br /&gt;
			  key		=&amp;gt;	$key,&lt;br /&gt;
			  language	=&amp;gt;	$param-&amp;gt;{language},&lt;br /&gt;
			  hash		=&amp;gt;	$param-&amp;gt;{hash},&lt;br /&gt;
			  variable	=&amp;gt;	undef,&lt;br /&gt;
		});&lt;br /&gt;
		if ($say_word)&lt;br /&gt;
		{&lt;br /&gt;
			$param-&amp;gt;{string}=~s/#!word!$key!#/$say_word/;&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			$param-&amp;gt;{string}=~s/#!word!$key!#/!!e[$key]!!/;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		die &amp;quot;Infinite loop detected while replacing #!word!...!# keys in the string: [$param-&amp;gt;{string}], exiting.\n&amp;quot; if $i &amp;gt; $an-&amp;gt;_error_limit;&lt;br /&gt;
		$i++;&lt;br /&gt;
	}&lt;br /&gt;
	return ($param-&amp;gt;{string});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# This takes a string with &amp;#039;#!var!x!#&amp;#039; keys, where &amp;#039;x&amp;#039; is an integer matching&lt;br /&gt;
# an entry in the passed array reference and uses the data from the array to&lt;br /&gt;
# replace the matching &amp;#039;#!var!x!#&amp;#039; entry.&lt;br /&gt;
sub _insert_vars&lt;br /&gt;
{&lt;br /&gt;
	my $self=shift;&lt;br /&gt;
	my $param=shift;&lt;br /&gt;
	&lt;br /&gt;
	my $an=$self-&amp;gt;parent;&lt;br /&gt;
	&lt;br /&gt;
	my $i=0;&lt;br /&gt;
	while ( $param-&amp;gt;{string} =~ /#!var!(.+?)!#/ )&lt;br /&gt;
	{&lt;br /&gt;
		my $val=$param-&amp;gt;{variable}-&amp;gt;[$1];&lt;br /&gt;
		if ( not defined $param-&amp;gt;{variable}-&amp;gt;[$1] )&lt;br /&gt;
		{&lt;br /&gt;
			# I can&amp;#039;t expect there to always be a defined value in&lt;br /&gt;
			# the @vals array at any given position so if it&amp;#039;s&lt;br /&gt;
			# blank I blank the key.&lt;br /&gt;
			$param-&amp;gt;{string}=~s/#!var!$1!#//;&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			chomp $val;&lt;br /&gt;
			$param-&amp;gt;{string}=~s/#!var!$1!#/$val/;&lt;br /&gt;
		}&lt;br /&gt;
		die &amp;quot;Infinite loop detected while injecting variables into the string: [$param-&amp;gt;{string}], exiting.\n&amp;quot; if $i &amp;gt; $an-&amp;gt;_error_limit;&lt;br /&gt;
		$i++;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	return ($param-&amp;gt;{string});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# This takes the path/name of an XML file containing AN::Tools type words and&lt;br /&gt;
# reads them into a hash reference.&lt;br /&gt;
sub read_words&lt;br /&gt;
{&lt;br /&gt;
	my $self=shift;&lt;br /&gt;
	my $param=shift;&lt;br /&gt;
	&lt;br /&gt;
	# This just makes the code more consistent.&lt;br /&gt;
	my $an=$self-&amp;gt;parent;&lt;br /&gt;
	&lt;br /&gt;
	# Clear any prior errors as I may set one here.&lt;br /&gt;
	$an-&amp;gt;Alert-&amp;gt;_set_error;&lt;br /&gt;
	&lt;br /&gt;
	# Setup my variables.&lt;br /&gt;
	my $file=0;&lt;br /&gt;
	my $hash=0;&lt;br /&gt;
	&lt;br /&gt;
	# Now see if the user passed the values in a hash reference or&lt;br /&gt;
	# directly.&lt;br /&gt;
	if (ref($param) eq &amp;quot;HASH&amp;quot;)&lt;br /&gt;
	{&lt;br /&gt;
		# Values passed in a hash, good.&lt;br /&gt;
		$file=$param-&amp;gt;{file} ? $param-&amp;gt;{file} : $self-&amp;gt;{FILE};&lt;br /&gt;
		$hash=ref($param-&amp;gt;{hash}) eq &amp;quot;HASH&amp;quot; ? $param-&amp;gt;{hash} : $self-&amp;gt;{HASH};&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		# Values passed directly.&lt;br /&gt;
		$file=$param;&lt;br /&gt;
		$hash=((defined $_[0]) &amp;amp;&amp;amp; (ref($_[0]) eq &amp;quot;HASH&amp;quot;)) ? shift : $self-&amp;gt;{HASH};&lt;br /&gt;
	}&lt;br /&gt;
	$file=$self-&amp;gt;{READ} if not $file;&lt;br /&gt;
	print &amp;quot;Reading: [$file] into: [$hash] (\$an-&amp;gt;data is: [&amp;quot;.$an-&amp;gt;data().&amp;quot;]\n&amp;quot;;&lt;br /&gt;
	&lt;br /&gt;
	# Make sure that the &amp;#039;file&amp;#039; exists and is readable.&lt;br /&gt;
	if ( not -e $file )&lt;br /&gt;
	{&lt;br /&gt;
		$an-&amp;gt;Alert-&amp;gt;error({&lt;br /&gt;
			fatal	=&amp;gt;	1,&lt;br /&gt;
			title	=&amp;gt;	&amp;quot;Unable to find the words file.&amp;#039;&amp;quot;,&lt;br /&gt;
			message	=&amp;gt;	&amp;quot;The words file: [$file] could not be found. Please check that it exists.&amp;quot;,&lt;br /&gt;
			code	=&amp;gt;	11,&lt;br /&gt;
			file	=&amp;gt;	&amp;quot;$THIS_FILE&amp;quot;,&lt;br /&gt;
			line	=&amp;gt;	__LINE__&lt;br /&gt;
		});&lt;br /&gt;
		# Return nothing in case the user is blocking fatal&lt;br /&gt;
		# errors.&lt;br /&gt;
		return (undef);&lt;br /&gt;
	}&lt;br /&gt;
	if ( not -r $file )&lt;br /&gt;
	{&lt;br /&gt;
		$an-&amp;gt;Alert-&amp;gt;error({&lt;br /&gt;
			fatal	=&amp;gt;	1,&lt;br /&gt;
			title	=&amp;gt;	&amp;quot;Unable to read the words file.&amp;#039;&amp;quot;,&lt;br /&gt;
			message	=&amp;gt;	&amp;quot;The words file: [$file] was found but could not be read. Please check that it exists.&amp;quot;,&lt;br /&gt;
			code	=&amp;gt;	12,&lt;br /&gt;
			file	=&amp;gt;	&amp;quot;$THIS_FILE&amp;quot;,&lt;br /&gt;
			line	=&amp;gt;	__LINE__&lt;br /&gt;
		});&lt;br /&gt;
		# Return nothing in case the user is blocking fatal&lt;br /&gt;
		# errors.&lt;br /&gt;
		return (undef);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	my $in_comment=0;	# Set to &amp;#039;1&amp;#039; when in a comment stanza that spans more than one line.&lt;br /&gt;
	my $in_data=0;		# Set to &amp;#039;1&amp;#039; when reading data that spans more than one line.&lt;br /&gt;
	my $closing_key=&amp;quot;&amp;quot;;	# While in_data, look for this key to know when we&amp;#039;re done.&lt;br /&gt;
	my $xml_version=&amp;quot;&amp;quot;;	# The XML version of the words file.&lt;br /&gt;
	my $encoding=&amp;quot;&amp;quot;;	# The encoding used in the words file. Should only be UTF-8.&lt;br /&gt;
	my $data=&amp;quot;&amp;quot;;		# The data being read for the given key.&lt;br /&gt;
	my $key_name=&amp;quot;&amp;quot;;	# This is a double-colon list of hash keys used to build each hash element.&lt;br /&gt;
	&lt;br /&gt;
	# Load IO::Handle if needed.&lt;br /&gt;
	$an-&amp;gt;_load_io_handle() if not $an-&amp;gt;_io_handle_loaded();&lt;br /&gt;
	&lt;br /&gt;
	# Read in the XML file with the word strings to load.&lt;br /&gt;
	my $read=IO::Handle-&amp;gt;new;&lt;br /&gt;
	my $shell_call=&amp;quot;&amp;lt;$file&amp;quot;;&lt;br /&gt;
	open ($read, $shell_call) || $an-&amp;gt;Alert-&amp;gt;error({&lt;br /&gt;
			fatal	=&amp;gt;	1,&lt;br /&gt;
			title	=&amp;gt;	&amp;quot;&amp;#039;AN::Tools::String-&amp;gt;read()&amp;#039; was not able to open the words file for reading.&amp;quot;,&lt;br /&gt;
			message	=&amp;gt;	&amp;quot;The AN::Tools::String methid &amp;#039;read&amp;#039; was not able to open the words file: [$file]. The error was: $!&amp;quot;,&lt;br /&gt;
			code	=&amp;gt;	13,&lt;br /&gt;
			file	=&amp;gt;	&amp;quot;$THIS_FILE&amp;quot;,&lt;br /&gt;
			line	=&amp;gt;	__LINE__&lt;br /&gt;
		});&lt;br /&gt;
	&lt;br /&gt;
	# If I have been asked to read in UTF-8 mode, do so.&lt;br /&gt;
	if ($an-&amp;gt;String-&amp;gt;force_utf8)&lt;br /&gt;
	{&lt;br /&gt;
		binmode $read, &amp;quot;encoding(utf8)&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	# Now loop through the XML file, line by line.&lt;br /&gt;
	while (&amp;lt;$read&amp;gt;)&lt;br /&gt;
	{&lt;br /&gt;
		chomp;&lt;br /&gt;
		my $line=$_;&lt;br /&gt;
		&lt;br /&gt;
		### Deal with comments.&lt;br /&gt;
		# Look for a clozing stanza if I am (still) in a comment.&lt;br /&gt;
		if (($in_comment) &amp;amp;&amp;amp; ( $line =~ /--&amp;gt;/ ))&lt;br /&gt;
		{&lt;br /&gt;
			$line=~s/^(.*?)--&amp;gt;//;&lt;br /&gt;
			$in_comment=0;&lt;br /&gt;
		}&lt;br /&gt;
		next if ($in_comment);&lt;br /&gt;
		&lt;br /&gt;
		# Strip out in-line comments.&lt;br /&gt;
		while ( $line =~ /&amp;lt;!--(.*?)--&amp;gt;/ )&lt;br /&gt;
		{&lt;br /&gt;
			$line=~s/&amp;lt;!--(.*?)--&amp;gt;//;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		# See if there is an comment opening stanza.&lt;br /&gt;
		if ( $line =~ /&amp;lt;!--/ )&lt;br /&gt;
		{&lt;br /&gt;
			$in_comment=1;&lt;br /&gt;
			$line=~s/&amp;lt;!--(.*)$//;&lt;br /&gt;
		}&lt;br /&gt;
		### Comments dealt with.&lt;br /&gt;
		&lt;br /&gt;
		### Parse data&lt;br /&gt;
		# XML data&lt;br /&gt;
		if ( $line =~ /&amp;lt;\?xml version=&amp;quot;(.*?)&amp;quot; encoding=&amp;quot;(.*?)&amp;quot;\?&amp;gt;/ )&lt;br /&gt;
		{&lt;br /&gt;
			$xml_version=$1;&lt;br /&gt;
			$encoding=$2;&lt;br /&gt;
			next;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		# If I am not &amp;quot;in_data&amp;quot; (looking for more data for a currently in use key).&lt;br /&gt;
		if (not $in_data)&lt;br /&gt;
		{&lt;br /&gt;
			# Skip blank lines.&lt;br /&gt;
			next if $line =~ /^\s+$/;&lt;br /&gt;
			next if $line eq &amp;quot;&amp;quot;;&lt;br /&gt;
			$line=~s/^\s+//;&lt;br /&gt;
			&lt;br /&gt;
			# Look for an inline data-structure.&lt;br /&gt;
			if (( $line =~ /&amp;lt;(.*?) (.*?)&amp;gt;/ ) &amp;amp;&amp;amp; ( $line =~ /&amp;lt;\/$1&amp;gt;/ ))&lt;br /&gt;
			{&lt;br /&gt;
				# First, look for CDATA.&lt;br /&gt;
				my $cdata=&amp;quot;&amp;quot;;&lt;br /&gt;
				if ( $line=~/&amp;lt;!\[CDATA\[(.*?)\]\]&amp;gt;/ )&lt;br /&gt;
				{&lt;br /&gt;
					$cdata=$1;&lt;br /&gt;
					$line=~s/&amp;lt;!\[CDATA\[$cdata\]\]&amp;gt;/$cdata/;&lt;br /&gt;
				}&lt;br /&gt;
				&lt;br /&gt;
				# Pull out the key and name.&lt;br /&gt;
				my $key=$line;&lt;br /&gt;
				my $name=$line;&lt;br /&gt;
				my $data=$line;&lt;br /&gt;
				$key=~s/^&amp;lt;(\w+).*/$1/;&lt;br /&gt;
				$name=~s/^&amp;lt;$key name=&amp;quot;(\w+).*/$1/;&lt;br /&gt;
				$data=~s/^&amp;lt;$key name=&amp;quot;$name&amp;quot;&amp;gt;(.*?)&amp;lt;\/$key&amp;gt;(.*)/$1/;&lt;br /&gt;
				&lt;br /&gt;
				# If I picked up data within a CDATA block,&lt;br /&gt;
				# push it into &amp;#039;data&amp;#039; proper.&lt;br /&gt;
				$data=$cdata if $cdata;&lt;br /&gt;
				&lt;br /&gt;
				# No break out the data and push it into the&lt;br /&gt;
				# corresponding keyed hash reference&lt;br /&gt;
				# &amp;#039;$an-&amp;gt;data&amp;#039;.&lt;br /&gt;
				$an-&amp;gt;_make_hash_reference($an-&amp;gt;data, &amp;quot;${key_name}::${key}::${name}::content&amp;quot;, $data);&lt;br /&gt;
				&lt;br /&gt;
				next;&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			# Look for a self-contained unkeyed structure.&lt;br /&gt;
			if (( $line =~ /&amp;lt;(.*?)&amp;gt;/ ) &amp;amp;&amp;amp; ( $line =~ /&amp;lt;\/$1&amp;gt;/ ))&lt;br /&gt;
			{&lt;br /&gt;
				my $key=$line;&lt;br /&gt;
				$key=~s/&amp;lt;(.*?)&amp;gt;.*/$1/;&lt;br /&gt;
				$data=$line;&lt;br /&gt;
				$data=~s/&amp;lt;$key&amp;gt;(.*?)&amp;lt;\/$key&amp;gt;/$1/;&lt;br /&gt;
				$an-&amp;gt;_make_hash_reference($an-&amp;gt;data, &amp;quot;${key_name}::${key}&amp;quot;, $data);&lt;br /&gt;
				next;&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			# Look for a line with a closing stanza.&lt;br /&gt;
			if ( $line =~ /&amp;lt;\/(.*?)&amp;gt;/ )&lt;br /&gt;
			{&lt;br /&gt;
				my $closing_key=$line;&lt;br /&gt;
				$closing_key=~s/&amp;lt;\/(\w+)&amp;gt;/$1/;&lt;br /&gt;
				$key_name=~s/(.*?)::$closing_key(.*)/$1/;&lt;br /&gt;
				next;&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			# Look for a key with an embedded value.&lt;br /&gt;
			if ( $line =~ /^&amp;lt;(\w+) name=&amp;quot;(.*?)&amp;quot; (\w+)=&amp;quot;(.*?)&amp;quot;&amp;gt;/ )&lt;br /&gt;
			{&lt;br /&gt;
				my $key=$1;&lt;br /&gt;
				my $name=$2;&lt;br /&gt;
				my $key2=$3;&lt;br /&gt;
				my $data=$4;&lt;br /&gt;
				$key_name.=&amp;quot;::${key}::${name}&amp;quot;;&lt;br /&gt;
				$an-&amp;gt;_make_hash_reference($an-&amp;gt;data, &amp;quot;${key_name}::${key}::${key2}&amp;quot;, $data);&lt;br /&gt;
				next;&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			# Look for a contained value.&lt;br /&gt;
			if ( $line =~ /^&amp;lt;(\w+) name=&amp;quot;(.*?)&amp;quot;&amp;gt;(.*)/ )&lt;br /&gt;
			{&lt;br /&gt;
				my $key=$1;&lt;br /&gt;
				my $name=$2;&lt;br /&gt;
				# Don&amp;#039;t scope &amp;#039;data&amp;#039; locally in case it spans&lt;br /&gt;
				# multiple lines.&lt;br /&gt;
				$data=$3;&lt;br /&gt;
				&lt;br /&gt;
				# Parse the data now.&lt;br /&gt;
				if ( $data =~ /&amp;lt;\/$key&amp;gt;/ )&lt;br /&gt;
				{&lt;br /&gt;
					# Fully contained data.&lt;br /&gt;
					$data=~s/&amp;lt;\/$key&amp;gt;(.*)$//;&lt;br /&gt;
					$an-&amp;gt;_make_hash_reference($an-&amp;gt;data, &amp;quot;${key_name}::${key}::${name}&amp;quot;, $data);&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					# Element closes later.&lt;br /&gt;
					$in_data=1;&lt;br /&gt;
					$closing_key=$key;&lt;br /&gt;
					&lt;br /&gt;
					$name=~s/^&amp;lt;$key name=&amp;quot;(\w+).*/$1/;&lt;br /&gt;
					$key_name.=&amp;quot;::${key}::${name}&amp;quot;;&lt;br /&gt;
					$data=~s/^&amp;lt;$key name=&amp;quot;$name&amp;quot;&amp;gt;(.*)/$1/;&lt;br /&gt;
					$data.=&amp;quot;\n&amp;quot;;&lt;br /&gt;
				}&lt;br /&gt;
				next;&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			# Look for an opening data structure.&lt;br /&gt;
			if ( $line =~ /&amp;lt;(.*?)&amp;gt;/ )&lt;br /&gt;
			{&lt;br /&gt;
				my $key=$1;&lt;br /&gt;
				$key_name.=&amp;quot;::$key&amp;quot;;&lt;br /&gt;
				next;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			### I&amp;#039;m in a multi-line data block.&lt;br /&gt;
			# If this line doesn&amp;#039;t close the data block, feed it&lt;br /&gt;
			# wholesale into &amp;#039;data&amp;#039;. If it does, see how much of&lt;br /&gt;
			# this line, if anything, is pushed into &amp;#039;data&amp;#039;.&lt;br /&gt;
			if ( $line !~ /&amp;lt;\/$closing_key&amp;gt;/ )&lt;br /&gt;
			{&lt;br /&gt;
				$data.=&amp;quot;$line\n&amp;quot;;&lt;br /&gt;
			}&lt;br /&gt;
			else&lt;br /&gt;
			{&lt;br /&gt;
				# This line closes the data block.&lt;br /&gt;
				$in_data=0;&lt;br /&gt;
				$line=~s/(.*?)&amp;lt;\/$closing_key&amp;gt;/$1/;&lt;br /&gt;
				$data.=&amp;quot;$line&amp;quot;;&lt;br /&gt;
				&lt;br /&gt;
				# If this line contain new-line control&lt;br /&gt;
				# characters, break the line up into multiple&lt;br /&gt;
				# lines and process them seperately.&lt;br /&gt;
				my $save_data=&amp;quot;&amp;quot;;&lt;br /&gt;
				my @lines=split/\n/, $data;&lt;br /&gt;
				&lt;br /&gt;
				# I use this to track CDATA blocks.&lt;br /&gt;
				my $in_cdata=0;&lt;br /&gt;
				&lt;br /&gt;
				# Loop time.&lt;br /&gt;
				foreach my $line (@lines)&lt;br /&gt;
				{&lt;br /&gt;
					# If I am in a CDATA block, check for&lt;br /&gt;
					# the closing stanza.&lt;br /&gt;
					if (( $in_cdata == 1 ) &amp;amp;&amp;amp; ( $line =~/]]&amp;gt;$/ ))&lt;br /&gt;
					{&lt;br /&gt;
						# CDATA closes here.&lt;br /&gt;
						$line =~s/]]&amp;gt;$//;&lt;br /&gt;
						$save_data.=&amp;quot;\n$line&amp;quot;;&lt;br /&gt;
						$in_cdata=0;&lt;br /&gt;
					}&lt;br /&gt;
					&lt;br /&gt;
					# If this line is a self-contained&lt;br /&gt;
					# CDATA block, pull the data out.&lt;br /&gt;
					# Otherwise, check if this line starts&lt;br /&gt;
					# a CDATA block.&lt;br /&gt;
					if (( $line =~/^&amp;lt;\!\[CDATA\[/ ) &amp;amp;&amp;amp; ( $line =~/]]&amp;gt;$/ ))&lt;br /&gt;
					{&lt;br /&gt;
						# CDATA opens and closes in this line.&lt;br /&gt;
						$line=~s/^&amp;lt;\!\[CDATA\[//;&lt;br /&gt;
						$line=~s/]]&amp;gt;$//;&lt;br /&gt;
						$save_data.=&amp;quot;\n$line&amp;quot;;&lt;br /&gt;
					}&lt;br /&gt;
					elsif ( $line =~/^&amp;lt;\!\[CDATA\[/ )&lt;br /&gt;
					{&lt;br /&gt;
						$line=~s/^&amp;lt;\!\[CDATA\[//;&lt;br /&gt;
						$in_cdata=1;&lt;br /&gt;
					}&lt;br /&gt;
					&lt;br /&gt;
					# If I am in a CDATA block, feed the&lt;br /&gt;
					# (sub)line into &amp;#039;save_data&amp;#039; wholesale.&lt;br /&gt;
					if ( $in_cdata == 1 )&lt;br /&gt;
					{&lt;br /&gt;
						# Don&amp;#039;t analyze, just store.&lt;br /&gt;
						$save_data.=&amp;quot;\n$line&amp;quot;;&lt;br /&gt;
					}&lt;br /&gt;
					else&lt;br /&gt;
					{&lt;br /&gt;
						# Not in CDATA, look for XML data.&lt;br /&gt;
						while (( $line =~ /&amp;lt;(.*?)&amp;gt;/ ) &amp;amp;&amp;amp; ( $line =~ /&amp;lt;\/$1&amp;gt;/ ))&lt;br /&gt;
						{&lt;br /&gt;
							# Found a value.&lt;br /&gt;
							my $key=$line;&lt;br /&gt;
							$key=~s/.*?&amp;lt;(.*?)&amp;gt;.*/$1/;&lt;br /&gt;
							$data=$line;&lt;br /&gt;
							$data=~s/.*?&amp;lt;$key&amp;gt;(.*?)&amp;lt;\/$key&amp;gt;/$1/;&lt;br /&gt;
							&lt;br /&gt;
							$an-&amp;gt;_make_hash_reference($an-&amp;gt;data, &amp;quot;${key_name}::${key}&amp;quot;, $data);&lt;br /&gt;
							$line =~ s/&amp;lt;$key&amp;gt;(.*?)&amp;lt;\/$key&amp;gt;//&lt;br /&gt;
						}&lt;br /&gt;
						$save_data.=&amp;quot;\n$line&amp;quot;;&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				&lt;br /&gt;
				# Knock out and new-lines and save.&lt;br /&gt;
				$save_data=~s/^\n//;&lt;br /&gt;
				if ( $save_data =~ /\S/s )&lt;br /&gt;
				{&lt;br /&gt;
					# Record the data in my &amp;#039;$an-&amp;gt;data&amp;#039;&lt;br /&gt;
					# hash reference.&lt;br /&gt;
					$an-&amp;gt;_make_hash_reference($an-&amp;gt;data, &amp;quot;${key_name}::content&amp;quot;, $save_data);&lt;br /&gt;
				}&lt;br /&gt;
				&lt;br /&gt;
				$key_name=~s/(.*?)::$closing_key(.*)/$1/;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		next if $line eq &amp;quot;&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
	$read-&amp;gt;close();&lt;br /&gt;
	&lt;br /&gt;
	# Set a couple values about this file.&lt;br /&gt;
	$self-&amp;gt;{FILE}-&amp;gt;{XML_VERSION}=$xml_version;&lt;br /&gt;
	$self-&amp;gt;{FILE}-&amp;gt;{ENCODING}=$encoding;&lt;br /&gt;
	&lt;br /&gt;
	# Return the number.&lt;br /&gt;
	return (1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{footer}}&lt;/div&gt;</summary>
		<author><name>Digimer</name></author>
	</entry>
</feed>