This chapter contains a series of projects, which are based on the content introduced by this book, and the early chapters have been expanded.
Most of the projects here compared to the previously experienced projects, and they fully demonstrate the application of new technologies and class libraries.
17.1 text processing
If you have an experience of C or C , then the most beginning of the initial ability to control the text of the Java control is doubtful. In fact, our most fear is that the speed is particularly slow, which may prevent us of our creation capabilities. However, Java corresponds to the tool (especially String class) with strong features, as shown in the example of this section (and performance also has a certain degree of increase).
As everyone is about to see, the purpose of establishing these examples is to solve some problems encountered during the preparation of this book. However, their ability is not only here. Through simple transformation, they can make them bigger in other occasions. In addition, they also reveal a Java feature that has not been emphasized before this book.
17.1.1 List of Extraction Codes
For each complete code list (not code segment), you will undoubtedly not notice that they start and end with special annotation marks ('//:' and '///: ~'). The reason why this marker information is to be able to automatically extract the code from this book to a compatible source file. In my previous book, I designed a system that automatically merged the test code file into the book. But for this book, I found a more convenient approach to paste the code into the book once passed the initial test. And because it is difficult to compile the pass, I edited the code in the book. But how to extract and test the code? This program is the key. If you intend to solve a problem with text processing, it is also very useful. This example also demonstrates many of the features of the String class.
I first saved all books into an independent file in the ASCII text format. The CodePackager program has two operating modes (there is a corresponding description in usageString): If you use the -p flag, the program checks an input file that contains the ASCII text (ie, the content of this book). It will traverse this file, follow the comment marks to extract the code, and use the file name located in the first line to determine what name is created. In addition, it will check the package statement when you need to place a file in a special directory (selected according to the path selected by the package statement).
But this is not enough. The program also tracks the package (package) name, thus monitoring changes in the chapter. Since all packages used in each chapter are starting with C02, C03, C04, etc., which chapters are used to mark they belong (except those who start with COM, they will be tracked for different chapters. Ignore) - As long as the first code list in each chapter contains a package, the CodePackager program can know the changes in each chapter and put the subsequent files in the new subdirectory.
When each file is extracted, it will be placed in a SourceCodeFile object, followed by placing that object into a collection (later explained this process). These SourceCodeFile objects can be saved in the file, which is the second use of this project. If you call the CodePackager directly, do not add a -P flag, it will use a "package" file as your input. That file will then be extracted (release) into a separate file. So the -p flag means that the extracted file has been "packed" into this single file.
But why should I use packaging files so troublesome? This is because different computer platforms save text information in a different manner. The biggest problem is the representation of the charm characters; of course, there may be other problems. However, Java has a special type of IO data stream - DataoutputStream - it can guarantee "If the data comes from, as long as you use a DataInputStream to collect this data, you can save them in the correct format of this unit." That is, Java is responsible for controlling all the details related to different platforms, which is the most charming of Java. So the -p flag can save all things to a single file and use a universal format. Users can download this file from the web and the Java program, then run the CodePackager for this file, and the -p flag is not specified, the file will be released to the correct place in the system (you can specify another subdirectory; otherwise, you will create it in the current directory. Subdirectory). To ensure that it does not leave a format related to a specific platform, we need to describe a file or path, we use the File object. In addition, there is a special security measures: put an empty file in each subdirectory; the name of the file pointed out how many files should be found in that subdirectories. Here is a complete code, which will be described in detail later:
959-968 page program
We noticed that the package statement has been commented as an comment. Since this is the first program in this chapter, the package statement is required, telling the CodePackager with it to another chapter. But it will become a problem with it in the bag. When we create a package, we need to link the result plugs to the same specific directory structure, this approach is applicable to most of this book. But here, the CodePackager program must be compiled and run in a dedicated directory, so the package statement is marked as a comment. But for CodePackager, it "looks" still like an ordinary package statement, because the program is not particularly complex, can not detect multi-line comments (there is no need to do so complicated, here only require convenience).
The first two classes are the "Support / Tools" class, and the role is to make the remaining part of the program more coincide and more convenient. The first is PR, which is similar to the PERROR library of ANSI C, and both can print an error prompt message (but also exiting the program). The second class is encapsulated by the creation process of the file. This process has been introduced in Chapter 10; everyone already knows that this will become very cumbersome and troublesome. To solve this problem, the programs provided in Chapter 10 are committed to the creation of the new category, but the "static" approach here has been used. In those methods, the normal violation will be captured and processed accordingly. These methods make the remaining code more refresh, easier to read.
The first class that helps solve the problem is SourceCodefile, which represents all the information (content, file names, and directories) of a source file (content, file name, and directory). It also includes a series of String constants, representing the beginning and end of a file; a tag used within the package file; the current system's newline break; file path separator (Note To use system.getproperty () to detect a local version What is); and a large number of copyright statements, it is extracted from the copyright.txt file below:
969-967 page program
When you extract files from a package file, the file separator used in the initial system will also be labeled in order to replace it with a symbol that the local system is applied.
The subdirect of the current chapter is saved in the Chapter field, which is initialized into C02 (everyone can pay attention to the list of Chapter 2, there is no packaging statement). The Chapter field changes only when a package (package) statement is found in the current file.
1. Build a package file
The first builder is used to extract a file from the ASCII text version of this book. Send a call (deeper place in the list) and check each row until you find the beginning of a list. At this time, it will create a sourceCodeFile object, pass the contents of the first line (read by calling code) to it, while also passing the BufferedReader object to extract the source list remaining in this buffer. .
From this time, everyone will find that the String method is frequently used. In order to extract the file name, you need to call the overload version of Substring (), so that it starts from one start offset, always read the end of the string, thereby forming a "substring". To calculate this starting index, you must first use length () to delete the total length of the string head end with TRIM (). The first line may also have some characters after the file name; they are detected with indexof (). If you don't find the characters we want to find, return -1; if those characters are found, return their first appearance. Note that this is also an overload version of INDEXOF (), using a string as a parameter, not a character.
After analyzing and saving the file name, the first line will be placed in string contents (the string is used to save the full body of the source list). Subsequently, the remaining code line is read and incorporated into the Contents string. Of course, things are not as simple as imagining, as specific situations need to be specially controlled. One case is an error check: If you encounter a StartMarker (start tag), indicating that the code list of the current operation does not set an end tag. This belongs to an error condition that needs to be exited.
Another special case is related to the package keyword. Although Java is a free form language, this program requires the package keyword to be in the order. If the package keyword is found, the package name is extracted by checking the space located at the beginning and the semicolon at the end (note can also be implemented separately, the method is to use the overloaded substring (), which makes it simultaneously check the start And end the index position). Subsequently, replace the number of points in the package with a specific file separator - of course, here you have to assume that the file separator has only one length of the character. Although this hypothesis may apply to all systems, once you encounter problems, you must don't forget to check it here.
The default operation is to connect each line to the contents, and there is a wrap character until an endmarker is encountered. This tag indicates that the builder should stop. If you encounter the end of the file before Endmarker, there is an error.
2. Extract from the package file
The second builder is used to restore the source code file from the package file. Here, the method as a caller does not have to worry about skipping some intermediate text. The package file contains all source code files, which are closely up closely. It is only a BufferedReader that needs to be passed to the builder, which represents "information source". The builder will extract the information you need. But there are some configuration information that starts at each code list, and their identity is pointed out with PackMarker (package tag). If the PackMarker does not exist, it means that the caller tries to use this builder with an error. Once you find PackMarker, you will peel it out and extract the directory name (end with a '#') and the file name (until the end of the line). No matter which case, the old separator will be replaced with a separator that is costly, which is implemented with the string replace () method. The old separator is placed at the beginning of the package file, and it can be seen in a slightly behind the code list to see how it extracts it.
The rest of the builder is very simple. It reads every row and merge it into the Contents until the endmarker is met.
3. Access to the list of programs
The next series of methods is a simple accessor: Directory (), filename () (Note that the method may have the same spelling and case forms as the field) and Contents (). Hasfile () is used to point out whether this object contains a file (soon you will know why you need this).
The last three methods are committed to writing this code list to a file - either write a package file via WritePacked () or write a Java source file with WriteFile (). WritePacked () needs the only thing that is DataoutputStream, which is open in other places, represents the file that is ready to be written. It first puts the header in the first line, then call WriteBytes () writes Contents into a "universal" format.
When you are ready to write a Java source file, you must first build your files first. This is implemented in IO.PSOpen (). We need to pass a File object to it, which not only contains file names, but also contains path information. But the question is: Is this path actually exist? Users may decide to place all source code directories into a completely different subdirectory, which may not exist. So before you officially write each file, you must call the file.mkdirs () method, build the directory path we want to write to the file. It can build the entire path once.
4. Accessories of the entire list
The list of organizational code is very convenient as the form of a child catalog, although this requires the first set of lists first in memory. The reason why the reason is to do, there is still a very persuasive reason: in order to build a "health" system. That is, when you create each subdirectory of the code list, an additional file will be added, and its name contains the number of files that should have in that directory.
The Dirmap class can help us achieve this effect and effectively demonstrate an overview of "multiple mapping". This is implemented by a hashtable, its "key" is a subdirectory that is ready to create, and "value" is a vector object that contains the SourceCodefile object in that specific directory. Therefore, we are not mapped (or corresponding) to a value here, but through the corresponding Vector, "multiple mapping" to a set of values. Although this sounds seems complicated, it is very simple and straightforward. You can see that most of the code of the Dirmap class is related to writing to the file, not related to "multiple mapping". The code related to it is only very small. A DirMap (directory map or corresponding) relationship can be created in two ways: The default builder assumes that we hope that the directory is expanded from the current location, and the other builder lets us specify an alternate "absolute" path for the start directory.
The add () method is a relatively dense place taken action. First extract Directory () from the SourceCodefile we want, then check the HashTable, see if the key has already been included. If not, add a new vector to the hash table and associate it with that button. At this time, no matter what the way is taken, Vector is already in place, you can extract it out to add SourceCodefile. Since VECTOR can be easily combined with the same hash table, we feel very convenient from both aspects.
When writing a packaging file, you need to open a file that is ready to write (as DataOutputStream is turned on, making the data "generic") and writes the header information related to the old separator in the first line. Then generate an enumeration (enumeration) to the HashTable button, and traverse, select each directory, and get the vector associated with that directory so that each SourceCodefile in the vector can write to the package file.
When using Write () Write the Java source file to their corresponding directory, the method used is almost entirely consistent with WritePackedFile (), because both methods simply call the appropriate method in SourceCodefile. But here, the root path will be passed to SourceCodefile.WriteFile (). After all files are written, the additional files that specify the number of writes in the name will also be written.
5. Main program
Those classes introduced earlier are used in CodePackager. Everyone first saw the use of the method string. Once the final user invoaches the program correctly, the string of introducing the correct usage will be printed. Calling this string is a usage () method, and you have to exit the program. Main () The only task is to determine what we want to create a packaging file or want to extract from a package file. Subsequently, it is responsible for the use of the correct parameters and calls the appropriate method.
When you create a packaging file, it is located by default in the current directory, so we create DIRMAP with the default builder. After opening the file, each of them will read, and check if it meets special conditions:
(1) If the lead is a starting mark for the source code list, create a new SourceCodeFile object. The builder reads into all the contents of the source list. The resulting handle will join DIRMAP directly.
(2) If the head is a end tag for the source code list, it indicates that an error occurs because the end tag should only be discovered by the SourceCodefile builder.
When extracting / release a packaging file, the extracted content can enter the current directory, or you can enter another alternate directory. So you need to create a DirMap object accordingly. Open the file and read the first line. Old file path separator information will be extracted from this line. Then create the first sourcecodefile object according to the input, which will join DirMap. As long as a file contains a file, the new SourceCodeFile object will be created and joined (after the last one created with light, it will return to return, then HASFILE () will return an error). 17.1.2 Check your uppercase
Although some projects involving text processing, the previous example is more convenient, but the items to be introduced below can play immediately because it performs a style check to ensure that our case is in the form of "facts" Java style standard. It opens each .java file in the current directory and extracts all class names and identifiers. If you find that there is a situation that does not meet the Java style, you will report to us.
In order to make this program correctly, you must first build a class name, as a "warehouse", responsible for accommodating all kinds of names in the standard Java library. To achieve this purpose, it is necessary to traverse all source code subdirectories for standard Java libraries and run ClassScanner at each subdirectory. As for parameters, the name of the warehouse file is provided (each time you use the same path and name) and command line switch -a, indicating that the class name should be added to the warehouse file.
In order to check your own code with a program, you need to run it and pass it to the path and name of the warehouse file you want to use. It examines all classes and identifiers in the current directory and tells us which does not comply with typical Java capitalization.
Pay attention to this program is not perfect. Sometimes it may report yourself to find a problem. But when we carefully check the code, it finds that there is nothing to change. Although this is a bit annoying, it is still much stronger than all the mistakes in the code.
The source code is listed below, and there is a detailed explanation:
974-980 page program
The MultiStringMap class is a special tool that allows us to bring a set of strings to each key item (mapping). As with the previous example, a hashtable is also used, but inherits are set this time. The lague table treats the keys as a single string of a vector value that is mapped to a VECTOR value. The role of the add () method is simple, responsible for checking if there is a key in the hash table. If there is no existence, it is placed therein. The getVector () method produces a VECTOR for a specific key; PrintValues prints all values one by Vector, which is very useful for programs.
To simplify the program, all class names from the standard Java library are all in a Properties object (from the standard Java library). Remember that the Properties object is actually a scatter list, which only accommodates String objects for keys and value items. However, only one method is called, we can save it to disk or recover from the disk. In fact, we only need a list of names, so all the same objects are used for keys and values.
For files in a specific directory, we use two multistringmap: Classmap and Identmap. In addition, when the program is started, it will load the standard class name warehouse to the Properties object named Classes. Once a new class name is discovered in the local directory, it will also add Classes and ClassMap. In this way, ClassMap can be used to traverse all kinds of locally directories, and can be used to check that the current tag is a class name (which is marked with the beginning of the object or method, so the next mark is collected until I meet A semicolon - and place them into Identmap. ClassScanner's default builder creates a list of file names (see Chapter 10) with a list of file names (using FileNameFilter's JavaFilter implementation, see Chapter 10). SCANLISTING () will then be called for each file name.
Inside the scanListing (), the source file is turned on and converted into a streamTokenizer. According to the Java Help Document, True is passed to SlashStartComments () and SlashSlashComments () should be to strip those comments, but doing so seems some problems (almost invalid in Java 1.0). In contrast, those rows are marked as a comment, and use another method to extract comments. To achieve this, '/' must be captured as a raw character, rather than letting StreamTokeinzer treat it as part of the annotation. At this point, you must use the OrdinaryChar () method to indicate the StreamTokenizer to take the correct operation. The same reason is also applicable to the point number ('.') Because we want way to call separate identifiers. But for the underline, it was originally treated by StreamTokenizer as a separate character, but it should be part of the identifier as part of the identifier, as it is used in Tt_eof, etc. in the static final value. Of course, this is only true for this special procedure. Wordchars () methods need to get a series of characters we want to add, leave them in a marker as a word. Finally, when analyzing a single-line annotation or gives up a line, we need to know when a changing action happens. So by calling EollssIGNIFICANT (TRUE), the wrapper (EOL) will be displayed instead of being absorbed by StreamTokenizer.
ScanListing () The remaining part will read and check the mark until the file end. Once NextToken () returns a final static value --streamTokenizer.tt_eof, it is marked that the file tail has arrived.
If the marker is a '/', it means that it may be a comment, so it is called Eatcomments () to handle this situation. The only thing we interested here is whether it is a word, of course, there may be other special circumstances.
If the word is a class (class) or interface (interface), then the next mark should represent a class or interface name and place it into classes and classmap. If the words are import or package, then we have no interest in the rest of this line. All other things are definitely an identifier (this is what we are interested), or a keyword (not interested in this, but they are definitely lowercase form, so they don't have to check them ". They will be added to Identmap. The DiscardLine () method is a simple tool for finding the line position. Note You must check the end of each time you get a new mark.
As long as a positive slash is hit in the main resolution cycle, the EATcomments () method will be called. However, this does not mean that it will definitely encounter a note, so you must extract the next marker, check it is a positive slash (then this line will be discarded), or an asterisk. But if both are, it means that the mark you just taken out will be sent back in the main resolution cycle! Fortunately, the pushback () method allows us to "press" input data streams. So when you call nextToken () in the main parsing cycle, it can correctly get what I have just returned.
For convenience, the classnames () method produces an array, which contains all the names in the Classes collection. This method is not used in the program, but it is very useful to debug code.
The next two methods are where practically check. In CheckClassNames (), class names are extracted from Classmap (please remember, ClassMap only contains the names in this directory, they organize according to the file name, so the file name may be printed with the error class name). To do this, you need to take out each associated vector and traverse it. Check if the first character is lowercase. If it is indeed lowercase, the corresponding error prompt message is printed.
In CHECKIDENTNAMES (), we use a similar way: Each identifier name is extracted from Identmap. If the name is not in the Classes list, it is considered that it is an identifier or keyword. A special case is checked: if the length of the identifier is equal to 3 or longer, and all characters are capitalized, this identifier is ignored because it may be a static final value, such as TT_EOF. Of course, this is not a perfect algorithm, but it assumes that we will finally notice that any full-size write identifier is not suitable.
This method is not reporting each identifier at the beginning of the uppercase character, but tracks those that have been reported in a Vector of ReportSet (). It treats vector as a "set", tells us whether a project is already in that collection. The project is generated by connecting the file name and the identifier. If the element is not in the collection, it will be added and then a report is generated.
The remaining part of the program list is composed of main (), which is responsible for controlling the command line parameters, and judges that we are preparing to build a "warehouse" consisting of a series of classes on the basis of the standard Java library, or want to check the written The correctness of those codes. No matter which case, a ClassScanner object will be created.
Whether you are preparing to build a "warehouse" or ready to use an existing warehouse, you must try to open the existing warehouse. By creating a File object and testing whether it is present, you can decide whether to open the file and load the classes this Properties list (using LOAD ()) in ClassScanner. Classes from the warehouse will be added to the classes found by the ClassScanner builder instead of it. If only one command line parameter is provided, it means that you want to check the class name and identifier name. But if two parameters (the second is "-a"), it indicates that you want to form a class name warehouse. In this case, you need to open an output file and write a list to a file with the Properties.Save () method while providing a file header information with a string. 17.2 Find tools
Chapter 11 describes the Java 1.1 new "reflection" concept, and use this concept to query a specific class method - either a complete list of all methods, or a subset of this list (name to us Keywords match). The biggest advantage that the example is that all methods can be automatically displayed, not forcing us to traverse the inheritance structure, check the basic class of each level. So, it is actually a valid tool we save program hours: Because most Java methods are specified very comprehensive and detailed, it can effectively identify those method names that contain a special keyword. If you find a name that meets the standard, you can find the online help documentation according to it.
But the 11th example also has a defect, it does not use AWT, just a pure command line application. Here, we are ready to make an improved GUI version, which can automatically refresh the output when we type characters, and allow us to cut and paste the operation in the output:
983-987 page program
Some things in the program have been seen before. Like many of the GUI programs of this book, this can be used as a separate application and can also be used as a program (applet). In addition, the StripQualifiers class is exactly the same as it in Chapter 11.
The GUI contains a "textfield" called Name, or enters the class name you want to find; also contains another text field, named searchfor, selectively enter a certain text, hope Find those text in the method list. Checkbox allows us to indicate that it is finally hoped to use the full name in the output or to delete the various qualified information in front. Finally, the results are shown in a TextArea.
Everyone will notice that this program does not use any buttons or other components, can't start searching with them. This is because regardless of the text field or the check box is subject to their "listener" object monitor. Just make a change, the result list will be updated immediately. If you change the text in the Name field, new text It will be captured in the Namel class. If the text is not empty, it is used to find the class in class.Forname (). Of course, the name may become incomplete during the text, and the class.forname () will Failure, which means it will "throw" a violation. The violation will be captured, and Textarea will be set to "Nomatch" (not consistent). But just type a correct name (case is also calculated) Class.Forname () will succeed, and getMethods () and getConstructors () will return an array that makes up by the Method and Constructionor objects. Each object in these arrays is converted into a string through TSTRING () There is a complete method or builder signature), and two lists will be merged into N - a separate string array. Array N belongs to a member of the DisplayMethods class, and is used to display when calling redisplay () Update. If you change the Checkbox or SearchFor component, their "listener" will simply call redisplay (). Redisplay () creates a temporary array, which contains a string (RS representative "result set named RS. "--Result set). The result set is either replicated directly from N (no Find key), either selectively replicates from the string containing the Find keyword. Finally check Strip Checkbox, see if the user is I hope to delete the excess part in the name (default is "yes"). If the answer is affirmative, do this with stripqualifiers.strip (); it is reversed, simply displaying the list.
In init (), you may think that there is a need for a lot of heavy work when setting the layout. In fact, the arrangement of the assembly may only require very little work. But the benefit of using BorderLayout is that it allows users to change the size of the window, and can make the textarea (text area), which means that we can change the size so that you can see longer.
When programming, everyone will find that it is especially necessary to make this tool in operation, because when trying to determine what method to call, it provides one of the best methods.
17.3 Complexity Theory
The predecessor of the procedure to be introduced is found by Larry O'brien original, and based on the "Boids" program prepared by Craig Reynolds in 1986, it was a special problem to demonstrate complexity theory, named " High "(Emergence).
The goal to be achieved here is to realize the population of animals by specifying a little simple rule for each animal. Every animal can see the entire environment and other animals in the environment, but it is only dedicated to a series of "group partners". Animal movement is based on three simple guidance behaviors:
(1) Separate: Avoid local group gatherings too crowded.
(2) Direction: Compliance with the universal direction of local group partners.
(3) Aggregation: Move toward the local group of partner groups.
More complex models can even include obstacles, animals can predict and avoid the ability to conflict with obstacles, so they can freely move around the fixed objects in the environment. In addition, animals may have their own special goals, which may cause the group to advance in a specific path. In order to simplify discussions, avoid obstacles and target search factors are not included in the model established here. Although the computer itself is relatively simple, and the rules adopted are quite simple, the results look real. That is, quite realistic behaviors "highlight" from this simple model.
The program provides:
989-995 page program
Although this is not perfect for the behavior of Craig Reynold's "BOIDS" example, it shows yourself unique. Comprehensive modification can be made by adjusting the numbers. As for more situations related to this cluster behavior, you can access the homepage of Craig Reynold - in that place, and even provide a public 3D display of Boids:
http://www.hmt.com/cwr/boids.html
In order to run this program as a block, set the following block signs in the HTML file:
995 page
17.4 Summary
Through this chapter, you know that you can use Java to do some more complicated things. It can also be seen in these examples, although Java must have its own limitations, but subject to the main performance (for example, after writing text handles, it will find that C version is much faster - this part is due to IO The library is not perfect; and when you read this book, the situation may have changed. But the limitations of Java are only, and its ability in language expression is unparalleled. Use Java, Almost anything we want. And at the same time, Java also made efforts in the convenience and readability of expression. So when using Java, it is generally not more common in other languages. Stateland. When using those languages, they will feel that they are like a loved old woman, which is pure, concise, concise! And through Java 1.2 JFC / SWING library, AWT's expression ability and ease of use of AWT have been further further Enhance.
17.5 practice
(1) (slightly difficulty) to change Fieldobeasts.java, so that it can remain fixed. Add some buttons, allow users to save and recover different status files and continue to run from where they break away. Please refer to CadState.java in Chapter 10, and then decide how to do it.
(2) (big homework) uses Fieldobeasts.java as the starting point, constructs an automated traffic simulation system.
(3) (big homework) as a starting point as a starting point, constructing a special tool, uses it to identify how to define but never used methods and fields.
(4) (big homework) uses JDBC to construct a contact management program. Let this program basis based on a flat file database, which contains contact information such as name, address, telephone number, E-mail address. A new name should be easily added to the database. When you type the name you want to find, use the name automatic filling technology introduced in Vlookup.java in Chapter 15.