Building a File System Example


The Composite pattern is simple in terms of its implementation, but its usage might be a little unclear as of yet. We've already discussed the details of how it works, but an example usually helps illustrate the concepts. In this example, we'll build a simple application that uses the file-system metaphor to show how the Composite pattern works.

This example uses the IFileSystemItem interface as well as the FileSystemItem, File, and Directory classes discussed earlier in this chapter. In addition, we'll create a FileSystemItemView class and a main class (CompositeExample). The application will load data from an XML file and use that data to allow the user to browse a graphical representation of a file system. The directories are represented by folder icons, and files are represented by white rectangles. The user can click a directory to browse the contents of the directory.

For the purposes of this example, we'll read the data from an XML file called fileSystem.xml with the following content:

<fileSystem>    <fileSystemItem type="Directory" name="Program Files" bytes="1024">       <fileSystemItem type="Directory" name="Adobe Illustrator">          <fileSystemItem type="File" name="Illustrator.exe" />       </fileSystemItem>    </fileSystemItem>    <fileSystemItem type="Directory" name="My Documents">       <fileSystemItem type="File" name="Document.txt" />       <fileSystemItem type="File" name="Image.jpg" />    </fileSystemItem> </fileSystem>


You can see that the root node is <fileSystem>, and contained within that are nested <fileSystemItem> tags. Each <fileSystemItem> tag is of type Directory or File. Directory nodes can contain nested elements whereas File nodes cannot. Each element has a name attribute as well. We'll load this XML file into the application and parse it into our composite structure.

Next we'll need a class that is a view for the File and Directory classes. The FileSystemItemView constructor accepts a parameter of type IFileSystemItem and then draws the correct icon and adds a label. Note that FileSystemItemView extends Sprite because it needs to be a display object.

package com.peachpit.aas3wdp.compositeexample.views {    import flash.display.Sprite;    import flash.text.TextField;    import flash.text.TextFieldAutoSize;    import flash.filters.BevelFilter;    import com.peachpit.aas3wdp.compositeexample.data.Directory;    import com.peachpit.aas3wdp.compositeexample.data.IFileSystemItem;    public class FileSystemItemView extends Sprite {       // The file or directory to display.       private var _item:IFileSystemItem;                 // The icon for the item - either a white rectangle or       // a folder icon       private var _icon:Sprite;           // The name of the item       private var _label:TextField;       // Return a reference to the file or directory       public function get data():IFileSystemItem {          return _item;       }       public function FileSystemItemView(item:IFileSystemItem) {          _item = item;          _icon = new Sprite();          // Test if the item is a Directory or File. Draw the          // appropriate icon for the item type          if(item is Directory) {             _icon.graphics.lineStyle();             _icon.graphics.beginFill(0xFFFF00);             _icon.graphics.drawRect(0, 10, 50, 30);              _icon.graphics.endFill();             _icon.graphics.beginFill(0xFFFF00);             _icon.graphics.drawRoundRect(0, 0, 25, 15, 5, 5);             _icon.graphics.endFill();             _icon.filters = [new BevelFilter()];          }          else {             _icon.graphics.lineStyle(0, 0x000000, 1);             _icon.graphics.beginFill(0xFFFFFF);             _icon.graphics.drawRect(0, 0, 40, 50);             _icon.graphics.endFill();          }          addChild(_icon);          // Add a label text field          _label = new TextField();          _label.text = _item.getName();          _label.autoSize = TextFieldAutoSize.LEFT;          _label.x = 50;          addChild(_label);       }       // This method allows you to override the label text       // value for special cases such as parent directories       // where you want to display a specific label rather than       // the name of the item       public function overrideLabel(label:String):void {          _label.text = label;       }    } }


Next we need to define a main class. In this example, the main class is called CompositeExample. The main class loads the XML file, parses it into the composite structure, and displays the contents of the top-level directory. When the user clicks a directory, the main class dispatches an event that updates the view.

package {    import flash.display.Sprite;    import flash.net.URLLoader;    import flash.net.URLRequest;    import flash.events.Event;    import flash.events.MouseEvent;    import com.peachpit.aas3wdp.compositeexample.data.Directory;    import com.peachpit.aas3wdp.compositeexample.data.File;    import com.peachpit.aas3wdp.compositeexample.data.FileSystemItem;    import com.peachpit.aas3wdp.iterators.IIterator;    import com.peachpit.aas3wdp.compositeexample.views.FileSystemItemView;    import com.peachpit.aas3wdp.compositeexample.data.IFileSystemItem;    public class CompositeExample extends Sprite {             // The top-level directory which contains all the        // child elements       private var _fileSystem:Directory;       // An array of all the views currently displayed       private var _itemViews:Array;             public function CompositeExample() {             // Load the XML          var loader:URLLoader = new URLLoader();          loader.addEventListener(Event.COMPLETE, onLoadXML);          loader.load(new URLRequest("fileSystem.xml"));          // Construct the top-level directory. Set the name,          // and set the parent to null. Setting the parent to          // null will indicate that there are no parent           // composite objects for this directory.          _fileSystem = new Directory();          _fileSystem.setName("File System");          _fileSystem.setParent(null);          _itemViews = new Array();       }       // When the XML loads parse the XML into the composite       // structure. The parseXmlToFileSystem() method accepts an       // XMLList parameter and a Directory parameter. It parses       // all the XMLList children into elements within the       // directory.       private function onLoadXML(event:Event):void {          XML.ignoreWhitespace = true;          var xml:XML = new XML(event.target.data);          parseXmlToFileSystem(xml.children(), _fileSystem);                          // Display the contents of the top-level directory             updateView(_fileSystem);          }          private function updateView(directory:Directory):void {             var i:uint;             // Loop through all the currently-displayed item             // views, and remove them.             for(i = 0; i < _itemViews.length; i++) {                removeChild(_itemViews[i]);                delete _itemViews[i];             }             _itemViews = new Array();            // Retrieve the iterator for the current directory            var iterator:IIterator = directory.iterator();            var itemY:Number = 0;            var item:IFileSystemItem;            var view:FileSystemItemView;            // If the directory has a parent, then add a view for            // the parent directory and override the label so            // it simply says Parent Directory. Add a click            // event listener so when the user clicks, it changes            // to the parent directory            if(directory.getParent() != null) {               view = new FileSystemItemView(directory.getParent());               view.overrideLabel("Parent Directory");               view.addEventListener(MouseEvent.CLICK, onClick);               addChild(view);               _itemViews.push(view);               itemY += view.height + 5;            }             // Loop through all the items in the directory. Add             // a view for each item. If the item is a directory,             // add a click event listener.             while(iterator.hasNext()) {                item = IFileSystemItem(iterator.next());                view = new FileSystemItemView(item);                view.y = itemY;                itemY += view.height + 5;                if(item is Directory) {                   view.addEventListener(MouseEvent.CLICK, onClick);                }                addChild(view);                _itemViews.push(view);             }          }                          private function parseXmlToFileSystem(xml:XMLList, directory:Directory):void {             var i:uint;             var item:FileSystemItem;             // Loop through all the children of the XMLList.             // If the item is a directory, then make a new             // directory and call parseXmlToFileSystem()             // recursively to populate the directory. Otherwise          // construct a file.          for(i = 0; i < xml.length(); i++) {             if(xml[i].@type == "Directory") {                item = new Directory();                parseXmlToFileSystem(xml[i].children(), Directory(item));             }             else {                item = new File();             }             item.setParent(directory);             item.setName(xml[i].@name);             directory.addItem(item);          }       }       // When the user clicks an item view, update the view to the       // contents of the directory that the user clicked.       private function onClick(event:MouseEvent):void {          updateView(Directory(event.currentTarget.data));       }    } }


When you run the application, you ought to see two folders initially: ProgramFiles and MyDocuments. If you click one of the folders, the view updates to display the contents of the directory as well as a folder icon with a ParentDirectory label. An example of the application is shown in Figure 8.1.

Figure 8.1. The sample application.





Advanced ActionScript 3 with Design Patterns
Advanced ActionScript 3 with Design Patterns
ISBN: 0321426568
EAN: 2147483647
Year: 2004
Pages: 132

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net