Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 94 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
94
Dung lượng
397,27 KB
Nội dung
Applied Namespace Examples | 345 Now let’s look at Caller, a class that wishes to access ShelteredClass’s restricted methods and variables. Having already seen the authorizedClasses array in ShelteredClass, we know that Caller is a legal class. In our example, Caller is also the main application class, so it extends Sprite. The Caller class creates an instance of ShelteredClass in its constructor method and assigns that instance to the variable shelteredObject. package { import flash.display.*; public class Caller extends Sprite { private var shelteredObject:ShelteredClass; public function Caller ( ) { shelteredObject = new ShelteredClass( ); } } } To invoke secretMethod( ) on ShelteredClass,aCaller object must first retrieve a refer- ence to the restricted namespace. To do so, the Caller object passes itself to getRestrictedNamespace( ) and assigns the result (either restricted or null) to a vari- able, key, for later use. var key:Namespace = shelteredObject.getRestrictedNamespace(this); Then, before calling secretMethod( ), Caller first checks whether key refers to a valid namespace. If it does, then Caller uses key as the namespace when invoking secureMethod( ): if (key != null) { shelteredObject.key::secureMethod( ); } For convenience, our Caller class wraps the code that calls secretMethod( ) in a method named callSecretMethod( ): public function callSecretMethod ( ):void { var key:Namespace = shelteredObject.getRestrictedNamespace(this); if (key != null) { shelteredObject.key:: secretMethod( ); } } Example 17-6 shows the entire code for the Caller class, including callSecretMethod( ) and another convenience method, displaySecret( ), which accesses the restricted vari- able secretData using the same basic technique. return null; } } } Example 17-5. The ShelteredClass class (continued) 346 | Chapter 17: Namespaces Example: Program Modes Our last example is an electronic dictionary that translates from Japanese to English and vice versa. The dictionary demonstrates program modes—perhaps the area of namespace programming in ActionScript with the greatest potential. When in “Japa- nese mode,” the dictionary returns English translations for Japanese queries; when in “English mode,” the dictionary returns Japanese translations for English queries. Each mode is represented by a namespace— japanese for Japanese-to-English mode and english for English-to-Japanese mode. Here are the participants in this example: japanese A namespace for Japanese-specific variables and methods english A namespace for English-specific variables and methods QueryManager class Performs searches for words SearchOptions class Contains the basic options for a search operation Example 17-6. The Caller class package { import flash.display.*; public class Caller extends Sprite { private var shelteredObject:ShelteredClass; public function Caller ( ) { shelteredObject = new ShelteredClass( ); callSecretMethod( ); displaySecret( ); } public function callSecretMethod ( ):void { var key:Namespace = shelteredObject.getRestrictedNamespace(this); if (key != null) { shelteredObject.key::secretMethod( ); } } public function displaySecret ( ):void { var key:Namespace = shelteredObject.getRestrictedNamespace(this); if (key != null) { trace(shelteredObject.key::secretData); } } } } Applied Namespace Examples | 347 JapaneseSearchOptions class Contains options specific to a Japanese search operation EnglishSearchOptions class Contains options specific to an English search operation JEDictionary class The main application class Let’s look at these participants one at a time, bearing in mind that this example is not fully functional, and uses placeholder code where actual database searches would occur. We’ll start with the japanese and english namespace definitions, whose code should be familiar by now: package { public namespace english = "http://www.example.com/jedict/english"; } package { public namespace japanese = "http://www.example.com/jedict/japanese"; } Next comes the QueryManager class, which defines two methods to look up a word, japanese::search( ) and english::search( ). The appropriate search method is invoked depending on the current mode of the program. Each search method accepts an options argument that specifies search options in the form of either a JapaneseSearchOptions or an EnglishSearchOptions object, respectively. Later, in the JEDictionary class, we’ll see that the search options are selected according to the cur- rent program mode. Here’s the code for QueryManager: package { public class QueryManager { japanese function search (word:String, options:JapaneseSearchOptions):Array { trace("Now searching for '" + word + "'.\n" + " Match type: " + options.getMatchType( ) + "\n" + " English language variant: " + options.getEnglishVariant( )); // Code here (not shown) would search the Japanese-to-English // dictionary and return the results, but we'll just return a // hard-coded list of results as a proof-of-concept: return ["English Word 1", "English Word 2", "etc"]; } english function search (word:String, options:EnglishSearchOptions):Array { trace("Now searching for '" + word + "'.\n" + " Match type: " + options.getMatchType( ) + "\n" + " Use kanji in results: " + options.getKanjiInResults( )); // Code here (not shown) would search the English-to-Japanese 348 | Chapter 17: Namespaces // dictionary and return the results, but we'll just return a // hard-coded list of results as a proof-of-concept: return ["Japanese Word 1", "Japanese Word 2", "etc"]; } } } Now let’s examine the three search-options classes: SearchOptions and its two sub- classes, JapaneseSearchOptions and EnglishSearchOptions. The SearchOptions class specifies how the program should look for the requested search string, either using an “exact match” (the matching word must be identical to the search string), a “starts-with match” (all matching words must start with the search string), or a “con- tains match” (all matching words must contain the search string). The different types of matches are represented by the constants MATCH_EXACT, MATCH_ STARTSWITH , and MATCH_CONTAINS. The match type for a given search can be set and retrieved via the methods setMatchType( ) and getMatchType( ). Here’s the SearchOptions class: package { public class SearchOptions { public static const MATCH_EXACT:String = "Exact"; public static const MATCH_STARTSWITH:String = "StartsWith"; public static const MATCH_CONTAINS:String = "Contains"; private var matchType:String; public function SearchOptions ( ) { // Default to exact matching. setMatchType(SearchOptions.MATCH_EXACT); } public function getMatchType ( ):String { return matchType; } public function setMatchType (newMatchType:String):void { matchType = newMatchType; } } } The JapaneseSearchOptions class extends SearchOptions, adding options relevant to Japanese-to-English searches only—namely, whether results should be returned in U.S. English or U.K. English. These two English variants are represented by the con- stants ENGLISH_UK and ENGLISH_US. The English variant for a given search can be set and retrieved via the methods setEnglishVariant( ) and getEnglishVariant( ). package { public class JapaneseSearchOptions extends SearchOptions { public static const ENGLISH_UK:String = "EnglishUK"; public static const ENGLISH_US:String = "EnglishUS"; Applied Namespace Examples | 349 private var englishVariant:String; public function JapaneseSearchOptions ( ) { setEnglishVariant(JapaneseSearchOptions.ENGLISH_UK); } public function getEnglishVariant ( ):String { return englishVariant; } public function setEnglishVariant (newEnglishVariant:String):void { englishVariant = newEnglishVariant; } } } Like JapaneseSearchOptions, the EnglishSearchOptions class extends SearchOptions, adding options relevant to English-to-Japanese searches only—namely, whether results should be returned in kanji (a ideographic character set) or hiragana (a pho- netic character set). The character set for a given search can be set and retrieved via the methods setKanjiInResults( ) and getKanjiInResults( ): package { public class EnglishSearchOptions extends SearchOptions { private var kanjiInResults:Boolean = false; public function getKanjiInResults ( ):Boolean { return kanjiInResults; } public function setKanjiInResults (newKanjiInResults:Boolean):void { kanjiInResults = newKanjiInResults; } } } Finally let’s turn to JEDictionary, the application’s main class, where most of the namespace magic happens. Skim the class code in Example 17-7, then we’ll study it line by line. Example 17-7. The JEDictionary class package { import flash.display.Sprite; public class JEDictionary extends Sprite { private var queryMan:QueryManager; japanese var options:JapaneseSearchOptions; english var options:EnglishSearchOptions; private var lang:Namespace; 350 | Chapter 17: Namespaces To begin, the application’s main class, JEDictionary extends Sprite: public class JEDictionary extends Sprite { To perform searches, JEDictionary creates a QueryManager instance, which it assigns to the variable queryMan: private var queryMan:QueryManager; Next, JEDictionary creates two variables, both with the local name options, but qual- ified by the japanese and english namespaces. These hold the search options that will be passed to the QueryManager class’s instance method search( ). Notice that their datatypes correspond to the type of search being performed: japanese var options:JapaneseSearchOptions; english var options:EnglishSearchOptions; public function JEDictionary( ) { queryMan = new QueryManager( ); japanese::options = new JapaneseSearchOptions( ); japanese::options.setMatchType(SearchOptions.MATCH_STARTSWITH); japanese::options.setEnglishVariant(JapaneseSearchOptions.ENGLISH_US); english::options = new EnglishSearchOptions( ); english::options.setMatchType(SearchOptions.MATCH_CONTAINS); english::options.setKanjiInResults(true); // Do a Japanese search setModeJapaneseToEnglish( ); findWord("sakana"); // Do an English search setModeEnglishToJapanese( ); findWord("fish"); } public function findWord (word:String):void { var words:Array = queryMan.lang::search(word, lang::options); trace(" Words found: " + words); } public function setModeEnglishToJapanese ( ):void { lang = english; } public function setModeJapaneseToEnglish ( ):void { lang = japanese; } } } Example 17-7. The JEDictionary class (continued) Applied Namespace Examples | 351 Then comes the definition of the important lang variable, which refers to the namespace corresponding to the current dictionary mode (either Japanese or English): private var lang:Namespace; That’s it for JEDictionary’s variables; now let’s examine its methods: setModeEnglishtoJapanese( ), setModeJapaneseToEnglish( ), and findWord( ). The setModeEnglishtoJapanese( ) and setModeJapaneseToEnglish( ) methods activate the different modes of the dictionary by setting the variable lang to the english namespace or japanese namespace, respectively: public function setModeEnglishToJapanese ( ):void { lang = english; } public function setModeJapaneseToEnglish ( ):void { lang = japanese; } The findWord( ) method uses QueryManager to perform a dictionary lookup using the appropriate search( ) method. The call to search( ) is the most important line of code in our dictionary example: queryMan.lang::search(word, lang::options) Notice that the namespace (the program mode) determines both the type of search to perform (the behavior) and the type of options to be used for that search (the data). When lang is set to japanese, then japanese::search( ) is invoked and passed a JapaneseSearchOptions object. When lang is set to english, then english::search( ) is invoked and passed an EnglishSearchOptions object. The result of the search( ) invocation is assigned to the local variable words and then displayed in a debugging message: public function findWord (word:String):void { var words:Array = queryMan.lang::search(word, lang::options); trace(" Words found: " + words); } For demonstration purposes, the JEDictionary constructor method performs two example dictionary searches (though, in a full-featured application, dictionary searches would normally be performed in response to user input). Searches are car- ried out by the application’s QueryManager instance, which is created in the con- structor, as follows: queryMan = new QueryManager( ); Default options for all Japanese-to-English searches and English-to-Japanese searches are also set in the constructor: japanese::options = new JapaneseSearchOptions( ); japanese::options.setMatchType(SearchOptions.MATCH_STARTSWITH); 352 | Chapter 17: Namespaces japanese::options.setEnglishVariant(JapaneseSearchOptions.ENGLISH_US); english::options = new EnglishSearchOptions( ); english::options.setMatchType(SearchOptions.MATCH_CONTAINS); english::options.setKanjiInResults(true); To perform a search, the constructor sets the dictionary mode, then passes the search string to the JEDictionary class’s instance method findWord( ): // Do a Japanese search setModeJapaneseToEnglish( ); findWord("sakana"); // Do an English search setModeEnglishToJapanese( ); findWord("fish"); According to the current dictionary mode, the appropriate search( ) method is called, and the appropriate search options are used. And that completes our dictionary! And it also completes our study of namespaces. Remember you can download the source code for the dictionary application and other examples from this chapter at http://www.moock.org/eas3/examples. Final Core Topics We’re almost finished with our exploration of the core ActionScript language. The coming two chapters cover two final subjects: creating and manipulating XML-based data and Flash Player security restrictions. 353 Chapter 18 CHAPTER 18 XML and E4X19 Since Flash Player 5, ActionScript has included tools for working with XML- structured data. In ActionScript 1.0 and ActionScript 2.0, XML data was created and manipulated with the variables and methods of the built-in XML class (e.g., firstChild, nextSibling, appendChild( ), etc.). The XML class was based on the W3C Document Object Model, or DOM, a standard for interacting with XML docu- ments programmatically (see http://www.w3.org/DOM). As of ActionScript 3.0, the toolset for creating and manipulating XML has been com- pletely overhauled. ActionScript 3.0 implements ECMAScript for XML (“E4X”), an official ECMA-262 language extension for working with XML as a native datatype. E4X seeks to improve the usability and flexibility of working with XML in ECMA- 262-based languages (including ActionScript and JavaScript). Understanding XML Data as a Hierarchy Before we can learn to manipulate XML data with E4X, we must first understand the general principle of XML as hierarchical data. Both the legacy XML class and E4X treat XML data as a hierarchical tree in which each element and text block is consid- ered a tree node (i.e., a branch or a leaf). For example, consider the XML fragment in Example 18-1. (An XML fragment is a section of XML excerpted from an XML document.) Example 18-1. An example XML fragment <BOOK ISBN="0141182806"> <TITLE>Ulysses</TITLE> <AUTHOR>Joyce, James</AUTHOR> <PUBLISHER>Penguin Books Ltd</PUBLISHER> </BOOK> 354 | Chapter 18: XML and E4X The elements <BOOK>, <TITLE>, <AUTHOR>, and <PUBLISHER>, and the text “Ulysses”, “Joyce, James”, and “Penguin Books Ltd” are all considered nodes on the tree, as depicted in Figure 18-1. The element <BOOK> is the root of the tree—known as the root node of the XML data structure. Every well-formed XML document must have an all-encompassing root element, such as <BOOK>, that contains every other element. When a node is contained by another node, the contained node is said to be a child of the containing node; conversely, the containing node is known as the child node’s parent. In our example, the <TITLE> element is a child of <BOOK>, and <BOOK> is <TITLE>’s parent. Perhaps surprisingly, <TITLE> is not the first child of <BOOK>; it is the second. The first child is actually the so-called insignificant whitespace (the new line and two spaces) in the XML source code between the <BOOK> and <TITLE> tags. In E4X, insignificant whitespace means any of the following four formatting characters: space ( \u0020), carriage return ( \u000D), line feed (\u000A), and tab (\u0009). In an XML tree, text blocks—even ones that contain whitespace only—are considered nodes on the tree. Accordingly, the <BOOK> element has not three children but seven, four of which are so-called whitespace nodes (text nodes that contain insignificant whitespace only). The <BOOK> node’s seven children are known as siblings of one another because they reside on the same level in the hierarchy. For example, we say that <TITLE>’s next sib- ling is a whitespace node, and <AUTHOR>’s previous sibling is another whitespace node. You can see how the text nodes get in the way when moving from sibling to sibling in a hierarchy. Fortunately, by default, whitespace nodes are ignored by the E4X parser. E4X lets us treat <AUTHOR> as <TITLE>’s next sibling, which is what we want in Figure 18-1. An example XML hierarchy <BOOK> <TITLE> <AUTHOR> <PUBLISHER> “Penguins Books Ltd” “Joyce, James” “Ulysses” whitespace whitespace whitespace whitespace [...]... Representing XML Data in E4X In E4X, XML data is represented by one of two native ActionScript datatypes, XML and XMLList and their corresponding classes, also named XML and XMLList Due to the introduction of the E4X XML datatype, the legacy XML class from ActionScript 1.0 and ActionScript 2.0 has been renamed to XMLDocument in ActionScript 3.0 and moved to the flash.xml package Each XML instance represents one... attribute saleEndsDate.@TIME does not exist, we would ideally like ActionScript to generate a “nonexistent attribute” error, but unfortunately the version of the E4X specification implemented by ActionScript 3.0 stipulates that references to nonexistent attributes should return an empty XMLList object rather than causing an error Future versions of ActionScript may improve this situation We’ve now covered the... we can use ActionScript s automatic datatype conversion to convert the value of any XML instance’s variables to a string (ActionScript s datatype conversion rules are described in Chapter 8.) Here’s the technique: var bookISBN:String = novel.@ISBN; In the preceding code, novel is an instance of a dynamic class (XML) Hence, when we assign its ISBN variable to the typed variable bookISBN, ActionScript. .. line breaks and quotation marks in the preceding XML literal are perfectly normal ActionScript knows they are part of the XML data, and interprets them as such Where possible, ActionScript even converts certain reserved characters to XML entities For details see the section “Using XML Entities for Special Characters.” ActionScript also allows dynamic expressions to be used within an XML literal so that... ) directly on the XMLList returned by novel.AUTHOR, ActionScript recognizes that the list has only one XML instance () and automatically forwards the setName( ) invocation to that instance As a result, the name of the sole element contained by novel.AUTHOR is changed from "AUTHOR" to "WRITER" In most cases, this sleight-of-hand performed by ActionScript makes XML code easier to write and more... expression novel.TITLE.toString( ) returns "Ulysses" because ActionScript recognizes that the XMLList referred to by novel.TITLE has only one XML instance () and automatically forwards the toString( ) invocation to that instance When accessing the content of a text node as a String, we can typically omit the explicit call to toString( ) because ActionScript invokes toString( ) automatically whenever... } } // Usage: previousSibling(someNode); 366 | Chapter 18: XML and E4X Example 18 -5 defines nextSibling( ), the companion custom method to the previousSibling( ) method defined in Example 18-4 Notice that the method adds code to check that the specified node actually has a next sibling before returning it Example 18 -5 A custom nextSibling( ) method public function nextSibling (theNode:XML):XML { if... a variable var novel:XML = Ulysses Joyce, James Penguin Books Ltd ; Creating XML Data with E4X | 357 When the preceding code runs, ActionScript generates a new E4X XML instance representing the literal XML fragment and assigns it to the variable novel To view the XML source code for an XML instance (such as the one... // Also Legal Accessing XML Data | 371 In the specific case of the illegal code saleEndsDate.@TIME-ZONE, ActionScript treats the hyphen as a subtraction operation, and interprets the expression to mean saleEndsDate.@TIME minus ZONE! In all likelihood, no variable (or method) named ZONE exists, and ActionScript will generate the following error message: Access of undefined property 'ZONE' However, if... an XML element has any child elements (e.g., ’s child ) or child text nodes (e.g., ’s child “Ulysses"), those children are wrapped in an XMLList by Representing XML Data in E4X | 355 their parent XML instance Each XMLList instance is an arbitrary collection of one or more XML instances For example, an XMLList might be any of the following: • A series of attributes or elements returned . means any of the following four formatting characters: space ( u 002 0), carriage return ( u 000 D), line feed (u 000 A), and tab (u 000 9). In an XML tree, text blocks—even ones that contain whitespace. restrictions. 35 3 Chapter 18 CHAPTER 18 XML and E4X19 Since Flash Player 5, ActionScript has included tools for working with XML- structured data. In ActionScript 1 .0 and ActionScript 2 .0, XML data. E4X XML datatype, the legacy XML class from ActionScript 1 .0 and ActionScript 2 .0 has been renamed to XMLDocument in ActionScript 3. 0 and moved to the flash.xml package. Each XML instance represents