Wednesday, October 28, 2009

Recipe 1.15. Word-Wrapping Lines of Text










Recipe 1.15. Word-Wrapping Lines of Text





Problem


You want to turn a string full of miscellaneous whitespace into a string formatted with linebreaks at appropriate intervals, so that the text can be displayed in a window or sent as an email.




Solution


The simplest way to add newlines to a piece of text is to use a regular expression like the following.



def wrap(s, width=78)
s.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n")
end

wrap("This text is too short to be wrapped.")
# => "This text is too short to be wrapped.\n"

puts wrap("This text is not too short to be wrapped.", 20)
# This text is not too
# short to be wrapped.

puts wrap("These ten-character columns are stifling my creativity!", 10)
# These
# ten-character
# columns
# are
# stifling
# my
# creativity!





Discussion


The code given in the Solution preserves the original formatting of the string, inserting additional line breaks where necessary. This works well when you want to preserve the existing formatting while squishing everything into a smaller space:



poetry = %q{It is an ancient Mariner,
And he stoppeth one of three.
"By thy long beard and glittering eye,
Now wherefore stopp'st thou me?}

puts wrap(poetry, 20)
# It is an ancient
# Mariner,
# And he stoppeth one
# of three.
# "By thy long beard
# and glittering eye,
# Now wherefore
# stopp'st thou me?



But sometimes the existing whitespace isn't important, and preserving it makes the result look bad:



prose = %q{I find myself alone these days, more often than not,
watching the rain run down nearby windows. How long has it been
raining? The newspapers now print the total, but no one reads them
anymore.}

puts wrap(prose, 60)
# I find myself alone these days, more often than not,
# watching the rain run down nearby windows. How long has it
# been
# raining? The newspapers now print the total, but no one
# reads them
# anymore.



Looks pretty ragged. In this case, we want to get replace the original newlines with new ones. The simplest way to do this is to preprocess the string with another regular expression:



def reformat_wrapped(s, width=78)
s.gsub(/\s+/, " ").gsub(/(.{1,#{width}})( |\Z)/, "\\1\n")
end



But regular expressions are relatively slow; it's much more efficient to tear the string apart into
words and rebuild it:



def reformat_wrapped(s, width=78)
lines = []
line = ""
s.split(/\s+/).each do |word|
if line.size + word.size >= width
lines << line
line = word
elsif line.empty?
line = word
else
line << " " << word
end
end
lines << line if line
return lines.join "\n"
end

puts reformat_wrapped(prose, 60)
# I find myself alone these days, more often than not,
# watching the rain run down nearby windows. How long has it
# been raining? The newspapers now print the total, but no one
# reads them anymore.





See Also


  • The Facets Core library defines String#word_wrap and String#word_wrap! methods













How This Book Is Organized











 < Day Day Up > 







How This Book Is Organized





The book is divided into 13 chapters, organized by subject:






Chapter 1, Master Projects and Solutions





Projects and solutions are used to organize files, executables, class

libraries, and anything else that makes up your application.

Consequently, learning how to get the most out of projects and

solutions can greatly improve your experience with Visual Studio.

This chapter covers how to get the most out of projects and

solutions, including getting down and dirty with the undocumented

format of project and solution files.






Chapter 2, Master the Editor





At its heart, Visual Studio is just an editor, but it is quite a

powerful editor. The Visual Studio editor includes features like

IntelliSense, Syntax Coloring, Outlining, and much more. Visual

Studio 2005 adds refactoring and code snippet functionality to the

already-feature-rich editor of Visual Studio. This chapter covers how

to get the most out of the editor by showing how to use these

features to the fullest, as well as adding additional functionality

to the editor through the use of third-party add-ins.






Chapter 3, Navigating Visual Studio





Visual Studio includes a staggering number of windows, toolbars,

commands, and editors. Visual Studio also includes an impressive

number of ways to easily navigate both the application and your

source code. This chapter covers how to easily move around the Visual

Studio application as well as how to navigate your own source code.






Chapter 4, Customizing Visual Studio





When writing code, it is nice to have things just the way you like

them, and Visual Studio gives you plenty of opportunities to

customize the application just the way you want it. This chapter

covers how to customize shortcut keys, toolbars, menus, the toolbox,

and much more. There is nothing better than getting it just the way

you want it.






Chapter 5, Debugging





A large part of developing an application is finding and removing all

the bugs in that application, which is where debugging comes in.

Visual Studio provides an excellent debugging experience, making it

easier than ever to find and fix bugs in your applications. This

chapter covers how to get the most out of the debugger including how

to debug not only your source code, but also T-SQL and scripting

languages.






Chapter 6, Speed Hacks





Developers are constantly looking for ways to write code faster and

more efficiently. This chapter covers a number of ways to write code

more efficiently, including a number of macros to help automatically

create connection strings, sign assemblies, update references, and

more. This chapter also covers how to use the command window, as well

as how to write and use custom tools to automatically generate code

like collections and configuration sections.






Chapter 7, Help and Research





As a modern developer, you need to be able to quickly find answers to

things you don't know, because it is simply

impossible to know everything any more. This chapter covers various

ways to get answers to your questions from within Visual Studio using

the default help system or using add-ins to add more options. Another

important part of development is being able to research how the

internals of your application function. This chapter covers a number

of applications that can be used to research how your application

functions, including what IL it generates, statistics on your code,

and what objects your application creates.






Chapter 8, Comments and Documentation





Code comments and documentation are very important parts of any

application. When developers are maintaining an application or

writing the next version of it, good comments and documentation are

an invaluable resource. Writing this documentation is not always a

pleasant experience though, since it is time consuming and sometimes

tedious. This chapter covers how to use XML comments in .NET to make

the creation of documentation easier, including a number of

timesaving add-ins and applications.






Chapter 9, Server Explorer Hacks





The Server Explorer is one of the more neglected portions of Visual

Studio. This chapter attempts to remedy that fact by covering how the

Server Explorer can be used to interface with databases, services,

and performance counters, as well as WMI.






Chapter 10, Work with Visual Studio Tools





Visual Studio includes a number of different tools that are not part

of the normal IDE. This chapter covers how you can access a special

command prompt, stress test applications, and generate code and UML,

as well as obfuscate code with the various tools included with Visual

Studio.






Chapter 11, Visual Studio Tools for Office





Visual Studio Tools for Office give you the opportunity to extend

Office applications using the .NET language of your choice. This

chapter covers the basics of using these tools, including a sample

Word application and a sample Excel application.






Chapter 12, Extending Visual Studio





The nice thing about an application like Visual Studio is that,

because its target audience is developers, the application authors

know that those developers will want to write code that extends the

functionality of the application. Visual Studio provides a number of

different ways that you can extend its functionality. This chapter

covers the Visual Studio extensibility model and how to create Visual

Studio add-ins, as well as how to extend Visual Studio through normal

Windows applications.






Chapter 13, Enhancing Visual Studio





A large number of add-ins and applications that enhance the

functionality of Visual Studio are available. This chapter covers

some of the better add-ins and applications that have not already

been covered in other chapters. Some of the add-ins covered give you

the ability to unit-test applications inside of Visual Studio, help

with writing Web Services, blog from Visual Studio, spellcheck your

code, and much more.





















     < Day Day Up > 



    10.4 Package Specification



    [ Team LiB ]





    10.4 Package Specification


    Programming has always involved the "packaging" of related programs. This includes compiling a group of programs into a single object library and configuring the code of a single subsystem under one source code directory. Configuration management tools allow us to package our software under logical and functional subject areas so developers can easily locate programs for check-out and check-in purposes.


    For some environments, the highest abstraction of a unit of software is a procedure. The management of the software repository then becomes the management of many individual programs. For other languages, procedures that collectively satisfy a major functional requirement compile into one object module, managed as one software unit. This is the case with C programming where multiple C functions compile as one C object file. Java packages contain Java classes and the compiler relies on a match between directory and package name.


    A programmer who needs to use a procedure must first identify the module and then identify the specific function, procedure, or method needed. The C language uses a header file, a separate file from the C algorithmic code, that defines the interface�this is the first step in learning how to use another programmer's C code.


    Complexity of a programming language and the programming environment hinders development. IDEs are very successful with helping developers quickly locate reusable programs that are part of the development environment. Many of these IDE tools allow you to add your newly developed code to the library repository�this enables a developer to search for a particular interface and easily locate the API of another developer. Oracle's JDeveloper and Procedure Builder are IDE tools that provide this type of rapid development environment support.


    Without the use of an IDE, the features of a language can lead to small amounts of code reuse. One of the most powerful features of PL/SQL is the simplicity of the package specification. The PL/SQL language requires that the interface to a program collection compile into a single program unit. This program unit, the package specification, is not complex. It simply defines the API. The package body contains the details of the logic.


    You can incorporate PL/SQL package specification in the design process. High-level design includes defining high-level interfaces and documenting what an implementation will do. The package specification identifies interfaces and includes and uses code and comments to define the interface. A set of package specifications can represent the complete top-level design of a system. A package body with pseudocode and comments can represent the detailed design.


    In theory, should you see the code to a package specification on a programmer's desk, you should not know whether they are at the tail end of the design phase or the beginning of the code phase. The package specification overlaps the design and coding phase. The package specification defines a set of interfaces and operations performed on a set of objects, such as tables in a schema. It is also a compiled object in the database, subject to syntax rules and database privileges.


    The package specification is a single ASCII file that compiles as a single program unit. The package body is also a single ASCII file. The body compiles only after a successful compilation of the specification. You can put the specification in the same file as the body.


    10.4.1 Syntax and Style


    The basic package specification syntax is:





    CREATE PACKAGE package_name IS
    Type definitions for records, index-by tables,
    varrays, nested tables
    Constants
    Exceptions
    Global variable declarations
    PROCEDURE procedure_name_1 (parameters & types);
    PROCEDURE procedure_name_2 (parameter & types);
    FUNCTION function_name_1 (parameters & types) RETURN type;
    END package_name;

    There is no order to the procedures and functions in the specification. Put them in a logical order that suits the developer.


    The package body will include the PL/SQL code for each of the subprograms in the specification. Each subprogram in the specification must have an accompanying subprogram body.


    The package specification can declare data types such as a record type, data declarations such as a record, and exceptions. All data objects declared in the package specification are global. Therefore, only declare what needs to be global.


    The PROCEDURE statement in the package body, including the subprogram name, parameter names, parameter modes, and parameter types, must match the PROCEDURE statement in the specification. The same holds true for the FUNCTION subprogram.


    The package body template for the preceding specification is:





    CREATE PACKAGE BODY package_name IS
    PROCEDURE procedure_name_1 (parameters & types)
    IS
    local variables
    BEGIN
    body of code
    END procedure_name_1;
    PROCEDURE procedure_name_2 (parameter & types)
    IS
    local variables
    BEGIN
    body_of_code
    END procedure_name_2;
    FUNCTION function_name_1 (parameters & types) RETURN type
    IS
    local variables
    BEGIN
    body of code
    RETURN statement;
    END function_name_1;
    END package_name;

    A package specification may declare exceptions. Let's modify PACKAGE_NAME so it declares an exception. The exception name is INVALID_OPERATION. The package code is the same, but with an exception declaration. Exceptions are sometimes declared all in the beginning of the spec, sometimes all at the bottom. Most important, comments should indicate which subprograms potentially raise which exceptions.





    CREATE PACKAGE package_name IS
    invalid_operation EXCEPTION;
    PROCEDURE procedure_name_1 (parameters & types);
    PROCEDURE procedure_name_2 (parameter & types);
    FUNCTION function_name_1 (parameters & types) RETURN type;
    END package_name;

    The user of PACKAGE_NAME has a question: "What procedures raise this exception?" Assuming the answer is PROCEDURE_NAME_1, the application code, should it choose to handle a possible exception, looks like this:





    BEGIN
    other code, etc
    package_name.procedure_name_1(parameters);
    other code, etc
    EXCEPTION
    WHEN package_name.invalid_operation THEN do something;
    END;

    A package specification can also declare type definitions such as records. If a procedure is to return a student name and status, the option exists to declare a record type in the specification, as follows:





    CREATE PACKAGE package_name IS

    TYPE student_rec_type IS RECORD (
    student_name students.student_name%TYPE,
    status students.status%TYPE);

    PROCEDURE get_student
    (stud_id IN students.student_id%TYPE
    stud_rec OUT student_rec_type);

    END package_name;

    The package users should drive the design. You should consider the user community when designing the specification. Overloading of procedures and functions is a frequent technique to making a package interface acceptable to a wide audience. A reasonable suggestion to the prior PACKAGE_NAME specification is to declare a function in the spec and have this return a student record type. Why? Because it can make the application using the package easier to write and easier to read. Such a modification changes the specification to the following:





    CREATE PACKAGE package_name IS

    TYPE student_rec_type IS RECORD (
    student_name students.student_name%TYPE,
    status students.status%TYPE);

    PROCEDURE get_student
    (stud_id IN students.student_id%TYPE
    stud_rec OUT student_rec_type);

    FUNCTION student
    (stud_id IN students.student_id%TYPE)
    RETURN student_rec_type;
    END package_name;

    10.4.2 Developing a Specification


    A Booch (Grady Booch) diagram, showing just one package in Figure 10-5, is an excellent paradigm for design. These diagrams are easy to whiteboard for technical group discussion. They provide a clear image of the software architecture. Booch diagrams specify what operations exist. This enables one to look at the system requirements and judge if the operations designed will satisfy the requirements.


    Figure 10-5. Students Package.


    We begin this section with a software requirement to implement procedures that perform operations on the STUDENTS table. Chapter 4 includes the DDL, entity diagram, and sample data for a student application.


    We have some software to write. First, we need to implement the following:


    • Add a new student.

    • Return the total number of students.


    We begin with a model depicting these operations. These operations belong together in a package because they each perform functions on the student's table. The package name will be STUDENTS_PKG.


    The development must transition from the graphical model to an interface design. We use the package specification as a vehicle for describing this interface. The following paragraphs illustrate the development toward an interface specification.


    ADD_STUDENT SUBPROGRAM

    Below is a list of the columns of the STUDENTS table. Also refer to the STUDENTS table DDL in the data model demo of Chapter 4 (p. 146).


    • STUDENT_ID

    • STUDENT_NAME

    • COLLEGE_MAJOR

    • STATUS

    • STATE

    • LICENSE_NO


    The STUDENT_ID column is the primary key and is generated with the sequence STUDENTS_PK_SEQ. This column value is determined in the body of the ADD_STUDENT procedure and will use the sequence attribute NEXTVAL during the INSERT statement.


    The three columns after STUDENT_ID are mandatory column values. The last two are optional. We should make STATE and LICENSE_NO optional parameters in the procedure call. These will have a default of NULL in the procedure interface.





    PROCEDURE add_student
    (v_student_name IN students.student_name%TYPE,
    v_college_major IN students.college_major%TYPE,
    v_status IN students.status%TYPE,
    v_state IN students.state%TYPE DEFAULT NULL,
    v_license_no IN students.license_no%TYPE DEFAULT NULL);

    An application program can use this interface with the following call:





    students_pkg.add_student(name, major, status);

    The interface also permits:





    students_pkg.add_student(name,major,status,state,license_no);

    NO_OF_STUDENTS SUBPROGRAM

    This should be a function. The requirement is to return an attribute of the entire student body. The following is a function with some flexibility. The user can use this function to get the number of students who have a specific major, a specific status, or a combination. The two parameters passed would be (1) the subject major description value from the MAJOR_LOOKUP table and (2) a status value of "Degree" or "Certificate."





    FUNCTION NO_OF_STUDENTS
    (v_major IN major_lookup.major_desc%TYPE DEFAULT NULL,
    v_status IN students.status%TYPE DEFAULT NULL)
    RETURN NUMBER;

    This interface permits the package user to write the following calls:





    -- get the number of students with an undeclared major.
    undeclared_major_count INTEGER :=
    students_pkg.no_of_students(v_major=> 'Undeclared ');

    -- get the total number of students.
    student_count INTEGER := students_pkg.no_of_students();

    -- get the number of degree-seeking biology students.
    biology_degrees INTEGER :=
    students_pkg.no_of_students
    (v_major => 'Biology',
    v_status => 'Degree');

    The STATUS column indicates the student's status with the school and has a CHECK constraint with valid values of "Degree" and "Certificate."


    PACKAGE SPECIFICATION

    The package specification now defines the interfaces. It initially appears to meet the requirements of adding a student and returning a count of students in the school. This is a starting point. We can add subprograms as needed. Additional procedures and functions can enhance the overall functionality of the package.





    CREATE OR REPLACE PACKAGE students_pkg IS
    PROCEDURE add_student
    (v_student_name IN students.student_name%TYPE,
    v_college_major IN students.college_major%TYPE,
    v_status IN students.status%TYPE,
    v_state IN students.state%TYPE DEFAULT NULL,
    v_license_no IN students.license_no%TYPE DEFAULT NULL);

    FUNCTION NO_OF_STUDENTS
    (v_major IN major_lookup.major_desc%TYPE DEFAULT NULL,
    v_status IN students.status%TYPE DEFAULT NULL)
    RETURN NUMBER;
    END students_pkg;




      [ Team LiB ]



      Section 13.2. Data Display Debugger










      13.2. Data Display Debugger


      The Data Display Debugger (DDD) is a graphical front end to GDB and other command line debuggers. DDD has many advanced features beyond simply viewing source code and stepping through a debug session. Figure 13-1 is a screen shot of the DDD's main screen.





      Figure 13-1. Data Display Debugger

      [View full size image]




      DDD is invoked as follows:


      $ ddd --debugger xscale_be-gdb webs


      Without the --debugger flag, DDD would attempt to invoke the native GDB on your development host, which is not what you want if you are planning to debug an application on your target system. The second argument on the DDD command line is the program you will be debugging. See the man page for DDD for additional details.


      Using the command tool as shown in Figure 13-1, you can step through your program. You can set breakpoints either graphically or via the GDB console window at the bottom of the DDD screen. For target debugging, you must first connect your debugger to the target system as we did in Listing 13-4, using the target command. This command is issued in the GDB window of the ddd main screen.


      When you are connected to the target, you can execute similar commands to the sequence described in the previous example to isolate the program failure. Figure 13-2 shows the DDD display during the later phase of this debugging session.




      Figure 13-2. Debug session in DDD

      [View full size image]




      Notice that in Figure 13-2 we have initiated the display of some important program variables that can help us narrow the cause of the segmentation fault. We can watch these variables as we step through the program using the command tool shown in the figure.


      DDD is a powerful graphical front end for GDB. It is relatively easy to use and widely supported for many development hosts. Consult Section 13.7.1 at the end of this chapter for a link to the GNU DDD documentation.












      Chapter A. Tutti-Fruti Ubuntu









      Chapter A. Tutti-Fruti Ubuntu






      In This Chapter

      • Kubuntu: The GNOMEless Ubuntu

      • Helping young learners with Edubuntu

      • The lightweight Xubuntu





      You say Ubuntu, I say Kubuntu. You say Edubuntu, I say Xubuntu. Why don't we just . . . Anyway, there's more to Ubuntu than just Ubuntu. As the song says, there are actually four variations of Ubuntu.


      This appendix introduces them all. Ubuntu makes it easy to download each version and burn a CD-ROM. You can make and boot any or all discs. It's fun to experiment and explore the diverse Ubuntu world and make the decision as to which one best suits your needs.





      Trying a Different Look: Kubuntu


      Kubuntu is Ubuntu that uses the K Desktop Environment (KDE) graphical desktop in place of GNOME. KDE and GNOME are similar in many ways. However, KDE is considered to use more advanced technology than GNOME.


      The ordinary computer user won't find that much difference between KDE and GNOME. Some people like KDE, others like GNOME, and yet others — like myself — don't care.


      Kubuntu provides the same range of applications, such as Openoffice.org, that Ubuntu does. New Kubuntu releases follow the same six-month schedule, too. Please browse www.kubuntu.org for more information about this product.


      The following instructions assume you're working from an Ubuntu computer with a writable CD-ROM or DVD drive.


      You need to download a Kubuntu CD-ROM image from an Ubuntu mirror to a computer from which you can burn the image to a writable CD. Follow these steps:

      1. Click your Firefox browser icon on the GNOME panel.

      2. Type www.kubuntu.org/download.php in the Location text box and press the Enter key.

      3. Click the Download link for the latest version.

      4. Click the link to your continent.

        The menu of download sites expands to different countries.

      5. Click your country.

        The menu expands to show universities and various organizations.

      6. Click any of the various university or organization links (that house Kubuntu mirrors).

        The list further expands to one or more actual download sites.

      7. Click a download site.

        The Opening Kubuntu-6.10 dialog opens.

      8. Click the Save File button.

        The Downloads dialog opens, showing a progress meter.

      9. Click the Cleanup button and close the dialog when the download finishes.


      After you download the Kubuntu disc image, you'll want to burn a CD-ROM. Follow these steps:

      1. Insert a writable CD-ROM in your computer's CD burner drive.

      2. From the GNOME menu bar, choose SystemAdministrationTerminal.

      3. Type the following command to burn the CD-ROM:


        sudo cdrecorder -v Desktop/kubuntu*iso



      When the burn finishes, you can either reboot directly from the new disc or eject the disc and use it to boot another machine. Your computer boots from the Kubuntu CD-ROM, just like an Ubuntu disc. After the computer boots, you see the screen in Figure A-1 .


      Figure A-1: The Kubuntu screen.





      If you're comfortable using Ubuntu, you should feel at home using the KDE interface in Kubuntu.









      5.4. Test First











       < Day Day Up > 







      5.4. Test First



      Having a good suite of tests is important�so important, that many developers advocate writing the tests for new code before a single line of the code itself! This is called test driven development, or TDD for short. Such tests represent the requirements that your code must satisfy in order to be considered correct.



      To see how Eclipse makes TDD simple, keep the unit test you just created, but delete the Factorial.java file (select it in the Package Explorer and press Delete). The editor for the FactorialTest class will shown an error immediately because the Factorial class is not defined anymore. This simulates the state you would be in if you had written your test class first.



      Put the text cursor on the first line that has an error and press Ctrl+1 (Edit Quick Fix). Select the "Create class 'Factorial'" option and press Enter. When the New Java Class dialog appears, press Enter to accept the defaults.



      Now, go back to the FactorialTest editor and note that the compiler complains that there is no factorial(int) method. Press Ctrl+1 to create one.



      Unfortunately, the current version of Eclipse is not always smart enough to figure out the right return type, so you may need to change the generated return type to be a double. Use a dummy return value (0.0) for now. At this point, Factorial.java should look something like this:





      public static double factorial(int i) {

      return 0.0;

      }







      Tip: Of course, this is not the right way to calculate a factorial, but all you want to do at this point is get the program to compile again.


      Now you have a test case, a little bit of code, and no errors�so try running the tests. Unsurprisingly, they fail. At this point in actual TDD, you would go back to the code being tested and fix it so that it passes the tests, then add another test, make that work, and repeat the process until done.



      Compare this technique with what most people typically do. They write a bunch of code first, then write a trivial little test program to exercise that code (maybe something with a main() method and a few println() statements). Once that test is working, they throw the test away and assume their class will never break again.



      Don't ever throw tests away! Nurture them, slowly add to them, and run them often, preferably as part of an automated build and test system. Techniques even exist to create unit tests for user interfaces.




      Tip: When you get a bug report from your users, your first impulse may be to fix the bug. Instead, stop and write a unit test that fails because of the bug. Then, change the code so the test works. This ensures your fix actually solves the problem and helps improve your tests over time.


      The JUnit view is covered in more detail in Part VII. If you want to learn more about unit testing best practices, see:






      http://www.junit.org





      JUnit home page








      http://www.testdriven.com





      Resource for test driven development



















         < Day Day Up > 



        3.1 Standard Algorithm











         < Day Day Up > 







        3.1 Standard Algorithm



        The standard pattern movement algorithm uses lists or arrays of encoded instructions, or control instructions, that tell the computer-controlled character how to move each step through the game loop. The array is indexed each time through the loop so that a new set of movement instructions gets processed each time through.



        Example 3-1 shows a typical set of control instructions.





        Example 3-1. Control instructions data structure




        ControlData {

        double turnRight;

        double turnLeft;

        double stepForward;

        double stepBackward;

        };








        In this example, turnRight and turnLeft would contain the number of degrees by which to turn right or left. If this were a tile-based game in which the number of directions in which a character could head is limited, turnRight and turnLeft could mean turn right or left by one increment. stepForward and stepBackward would contain the number of distance units, or tiles, by which to step forward or backward.



        This control structure also could include other instructions, such as fire weapon, drop bomb, release chaff, do nothing, speed up, and slow down, among many other actions appropriate to your game.



        Typically you set up a global array or set of arrays of the control structure type to store the pattern data. The data used to initialize these pattern arrays can be loaded in from a data file or can be hardcoded within the game; it really depends on your coding style and on your game's requirements.



        Initialization of a pattern array, one that was hardcoded, might look something such as that shown in Example 3-2.





        Example 3-2. Pattern initialization




        Pattern[0].turnRight = 0;

        Pattern[0].turnLeft = 0;

        Pattern[0].stepForward = 2;

        Pattern[0].stepBackward = 0;

        Pattern[1].turnRight = 0;

        Pattern[1].turnLeft = 0;

        Pattern[1].stepForward = 2;

        Pattern[1].stepBackward = 0;

        Pattern[2].turnRight = 10;

        Pattern[2].turnLeft = 0;

        Pattern[2].stepForward = 0;

        Pattern[2].stepBackward = 0;

        Pattern[3].turnRight = 10;

        Pattern[3].turnLeft = 0;

        Pattern[3].stepForward = 0;

        Pattern[3].stepBackward = 0;

        Pattern[4].turnRight = 0;

        Pattern[4].turnLeft = 0;

        Pattern[4].stepForward = 2;

        Pattern[4].stepBackward = 0;

        Pattern[5].turnRight = 0;

        Pattern[5].turnLeft = 0;

        Pattern[5].stepForward = 2;

        Pattern[5].stepBackward = 0;

        Pattern[6].turnRight = 0;

        Pattern[6].turnLeft = 10;

        Pattern[6].stepForward = 0;

        Pattern[6].stepBackward = 0;

        .

        .

        .








        In this example, the pattern instructs the computer-controlled character to move forward 2 distance units, move forward again 2 distance units, turn right 10 degrees, turn right again 10 degrees, move forward 2 distance units, move forward again 2 distance units, and turn left 10 degrees. This specific pattern causes the computer-controlled character to move in a zigzag pattern.



        To process this pattern, you need to maintain and increment an index to the pattern array each time through the game loop. Further, each time through the loop, the control instructions corresponding to the current index in the pattern array must be read and executed. Example 3-3 shows how such steps might look in code.





        Example 3-3. Processing the pattern array




        void GameLoop(void)

        {

        .

        .

        .

        Object.orientation + = Pattern[CurrentIndex].turnRight;

        Object.orientation -- = Pattern[CurrentIndex].turnLeft;

        Object.x + = Pattern[CurrentIndex].stepForward;

        Object.x -- = Pattern[CurrentIndex].stepBackward;

        CurrentIndex++;

        .

        .

        .

        }








        As you can see, the basic algorithm is fairly simple. Of course, implementation details will vary depending on the structure of your game.



        It's also common practice to encode several different patterns in different arrays and have the computer select a pattern to execute at random or via some other decision logic within the game. Such techniques enhance the illusion of intelligence and lend more variety to the computer-controlled character's behavior.















           < Day Day Up > 



          Using Symmetric (Private) Key Encryption











           < Day Day Up > 





          Using Symmetric (Private) Key Encryption



          As mentioned previously symmetric key encryption uses a single private key for both encryption and decryption. To provide support for this type of encryption, the BCL defines the System::Security::Cryptography::SymmetricAlgorithm abstract class, which is the base class for all symmetric algorithm classes. There are wrapper classes for several popular symmetric cryptographic algorithms, all of which derive from this SymmetricAlgorithm class:



          • System::Security::Cryptography::DES

          • System::Security::Cryptography::RC2

          • System::Security::Cryptography::Rijndael

          • System::Security::Cryptography::TripleDES



          Essentially each of those classes are abstract base classes for their respective algorithms, and any class that implements any of those algorithms must derive from these classes. For example, a class called DESCryptoServiceProvider (derived from the DES class) provides a wrapper for the DES implementation provided by the Cryptographic service provider.



          Private Key Encrypting/Decrypting Demo Using DES



          The CryptoStream class is used to perform cryptographic transformations on any data stream and is under the System::Security::Cryptography namespace. It derives from System::IO::Stream, and thus we can call any Stream methods on a CryptoStream object just as if it were a network or file stream object. The CryptoStream constructor is as follows:





          CryptoStream(

          Stream* stream,

          ICryptoTransform* transform,

          CryptoStreamMode mode

          );



          The first parameter is a stream object that may be a file stream, a memory stream, or a network stream, and it's on this stream that the cryptographic transformation is performed.



          The second argument is an ICryptoTransform object that defines the cryptographic transform that is to be performed on the stream. Any class that derives from the SymmetricAlgorithm has a CreateEncryptor method that returns an ICryptoTransform object. Thus, to perform a DES transform on the data stream, instantiate a DESCryptoServiceProvider object and call CreateEncryptor on it, or, if you want to perform a Rijndael transformation, then instantiate a RijndaelManaged object and call CreateEncryptor. In fact you may also pass any class derived from the HashAlgorithm class, because they will all be implementing the ICryptoTransform interface, though the issue would be that hash algorithms are not key-based, and thus the security of the encryption would be considerably reduced.



          The third and last parameter for the CryptoStream constructor is a CrytopStreamMode enumeration value that specifies either read mode (CryptoStreamMode::Read) or write mode (CryptoStreamMode::Write).



          Now let's look at what it takes to encrypt a file. We'll look at a function with the following syntax:





          void EncryptFile(

          String* inputFile,

          String* outputFile

          );



          As you can see, this file takes only two parameters�an input file name and an output file name. However, before I throw some code at you, let's look at the generic steps involved in encrypting a file and the details of how the EncryptFile accomplished each given step.





















          1. Acquire a key value:

            One of the first tasks is to obtain a key value used to encrypt the file. In the case of the EncryptFile function, this value is hard-coded in the function in the form of a String object. Since the Cryptography class works with byte arrays, this value is then converted to a byte array using the incredibly useful System::Text::Encoding class. (You should note that while this function has a hard-coded key value for the sake of simplicity, you might want to add this value to the parameter list.)



          2. Construct the FileStream objects for input and output:

            As you learned in Chapter 3, the FileStream object allows a generic means of reading and writing data to physical files. The EncryptFile function first constructs a FileStream object for the specified input file with read access. Then the function constructs a FileStream object for the specified output file name with create access.



          3. Construct a SymmetricAlgorithm-derived object:

            While you can obviously select whatever algorithm you need for your particular application, I've chosen the DESCryptoServiceProvider object to support the DES algorithm. (As you might guess from the class name, the DESCryptoServiceProvider is an encapsulation of the CryptoAPI support for DES.)



          4. Construct a symmetric encryptor object with the specified key and IV:

            All SymmetricAlgorithm-derived classes must implement the pure virtual CreateEncryptor function that takes key and IV parameters. This function returns an ICryptoTransform interface.



          5. Construct a CryptoStream object:

            At this point, we have two of the main ingredients needed to encrypt a file: a FileStream object for the output and an ICryptoTransform interface. These two objects (along with a CryptoStreamMode enumeration value of Write) are then passed to the CryptoStream constructor. As you might imagine from the discussion of streams in Chapter 3, the CryptoStream class handles all the details of how to output the data (in the specified encrypted form) to the indicated output file name.



          6. Read input data:

            Obviously this is application-specific, but, as mentioned in Step 2, the EncryptFile function presented here attaches a FileStream to the specified input file name. After construction of the CryptoStream object, the function simply reads through the input FileStream object, reading a block of 128 bytes at a time.



          7. Write data to the CryptoStream:

            As each of the 128-byte blocks are read, they are output to the CryptoStream using the CryptoStream::Write method. Internally, the CryptoStream object encrypts the data in the chosen format and outputs it to the underlying physical files.



          8. Close all opened Stream objects:

            In the case of the EncryptFile function, those would be the input and output FileStream objects and the CryptoStream object.



          That's it. Eight simple steps (about 50 lines of C++ code) to do something as advanced as taking an input file, opening it, reading every byte, encrypting every byte, and then writing the encrypted data to disk. At this point, let's see the actual C++ code for the EncryptFile (Listing 4-1). (Note that the comments in the code are in bold to make it easier to tie the aforementioned file-encryption steps to the actual implementation code.)



          Listing 4-1. The EncryptFile function outputs encrypted data from a specified input file to a specified output using a supplied private key.






          void EncryptFile(String* inputFileName, String* outputFileName)

          {

          #pragma push_macro("new")

          #undef new

          CryptoStream *cryptoStream;

          FileStream* outputFileStream;

          FileStream* inputFileStream;



          try

          {

          // Step #1 : Acquire a keyByteArray value - in this case hard-coded.

          String* keyString = S"KeyAbcGG";

          Byte keyByteArray[] = Text::Encoding::Default->GetBytes(keyString);



          // Step #2: Construct the FileStream objects for input and output

          inputFileStream = new FileStream(inputFileName,

          FileMode::Open,

          FileAccess::Read);



          outputFileStream = new FileStream(outputFileName,

          FileMode::Create,

          FileAccess::Write);



          // Step #3: Construct a SymmetricAlgorithm-derived object

          DESCryptoServiceProvider *serviceProvider =

          new DESCryptoServiceProvider();



          // Step #4: Construct a symmetric encryptor object with the

          // specified key and IV

          ICryptoTransform* encryptor =

          serviceProvider->CreateEncryptor(keyByteArray,keyByteArray);



          // Step #5: Construct a CryptoStream object

          cryptoStream = new CryptoStream(outputFileStream,

          encryptor, CryptoStreamMode::Write);



          Byte bytesread[] = new Byte[129];



          // Step #6: Read input data

          while(int n = inputFileStream->Read(bytesread, 0, 128))

          {

          // Step #7: Write data to the CryptoStream

          cryptoStream->Write(bytesread, 0, n);

          }

          }

          catch(Exception* e)

          {

          throw e;

          }

          __finally

          {

          // Step 8: Close all opened Stream objects

          cryptoStream->Close();

          outputFileStream->Close();

          inputFileStream->Close();

          }

          #pragma pop_macro("new")

          }



          Now, let's turn our attention to the task of decrypting data. For this operation, I've included a function called DecryptFile:





          void DecryptFile(

          String* encryptedfile,

          String* decryptedfile

          )



          Once again, I'll first list the basic steps of decrypting a file and outputting the results to another file and then present an implementation of that list in the form of the DecryptFile function.





















          1. Acquire a key value:

            As with encrypting a file using symmetric key encryption, a private key is needed to decrypt the file. Obviously, the key used to decrypt the file must match exactly the one that was used to encrypt. As with the EncryptFile function, I've hard-coded the value of the key in the DecryptFile for the sake of simplicity. (You'll want to change this key in your production environment.) This value is converted from its String object form to a byte array suitable for consumption by the BCL cryptography classes.



          2. Construct the FileStream objects for input and output:

            As mentioned earlier in this section the CryptoStream object that will be used to decrypt the data can be constructed from a Stream�including a file-based FileStream. Since our input is a physical file, we must first construct a FileStream using the specified input file name. At the same time, another FileStream object is created for the output.



          3. Construct a SymmetricAlgorithm-derived object:

            Obviously the algorithm used when decrypting a file must match the algorithm used when that file was encrypted. Since the EncryptFile function used the BCL wrapper for the CryptoAPI implementation of DES encryption, the DecryptFile function utilizes the DESCryptoServiceProvider here.



          4. Construct a symmetric decryptor object with the specified key and IV:

            All SymmetricAlgorithm-derived classes must implement the pure virtual CreateDecryptor function that takes key and IV parameters. This function returns an ICryptoTransform interface that will do the actual data decryption.



          5. Construct a CryptoStream object:

            At this point, we have two of the main ingredients needed to decrypt a file�a FileStream object for the input and an ICryptoTransform interface. These two objects (along with a CryptoStreamMode enumeration value of Read) are then passed to the CryptoStream constructor.



          6. Read input data:

            Utilizing the CryptoStream::Read method, data is read 128 bytes at a time into a Byte array. Once again, there is no decryption work on your end, as the transformer interface is doing the heavy lifting for you.



          7. Write data to the CryptoStream:

            This is obviously application-specific, but in the case of the DecryptFile function, each 128-byte block that is read via the CryptoStream is written to a FileStream attached to a physical file whose name was passed as a parameter to the function.



          8. Close all opened Stream objects:

            In the case of the DecryptFile function, those would be the input and output FileStream objects and the CryptoStream object.



          Once again, we have eight easy-to-follow steps for reading data from a file, decrypting it, and writing it to another file. Listing 4-2 shows the code for the DecryptFile (again, the comments are in bold so that you can match these steps with the actual C++ implementation).



          Listing 4-2. The DecryptFile function takes input from a physical file and outputs DES decrypted data to a specified file name using a supplied private key.






          void DecryptFile(String* inputFileName, String* outputFileName)

          {

          #pragma push_macro("new")

          #undef new

          CryptoStream *cryptoStream;

          FileStream* outputFileStream;

          FileStream* inputFileStream;



          try

          {

          // Step #1: Acquire a keyByteArray value - in this case hard-coded.

          String* keyString = S"KeyAbcGG";

          Byte keyByteArray[] = Text::Encoding::Default->GetBytes(keyString);



          // Step #2: Construct the FileStream objects for input and output

          inputFileStream = new FileStream(inputFileName,

          FileMode::Open,

          FileAccess::Read);



          outputFileStream = new FileStream(outputFileName,

          FileMode::Create,

          FileAccess::Write);



          // Step #3: Construct a SymmetricAlgorithm-derived object

          DESCryptoServiceProvider* serviceProvider =

          new DESCryptoServiceProvider();



          // Step #4: Construct a symmetric decryptor object with the

          // specified key and IV

          ICryptoTransform* decryptor =

          serviceProvider->CreateDecryptor(keyByteArray, keyByteArray);



          // Step #5: Construct a CryptoStream object

          cryptoStream = new CryptoStream(inputFileStream,

          decryptor,

          CryptoStreamMode::Read);



          Byte bytesRead[] = new Byte[129];



          // Step #6: Read input data

          while(int n = cryptoStream->Read(bytesRead, 0, 128))

          {

          // Step #7: Write data to the CryptoStream

          outputFileStream->Write(bytesRead,0,n);

          }

          }

          catch(Exception* e)

          {

          throw e;

          }

          __finally

          {

          // Step 8: Close all opened Stream objects

          cryptoStream->Close();

          outputFileStream->Close();

          inputFileStream->Close();

          }

          #pragma pop_macro("new")

          }



          It's one thing to present an allegedly helpful function (or two in this case), but let's take another step and see these functions at work in a demo application that allows you to both encrypt and decrypt files using a private key encryption algorithm�specifically, DES.





























          1. Create an MFC dialog-based application called DESDemo.

          2. Update the Project properties to support Managed Extensions.

          3. Open the stdafx.h file and add the following .NET support directives to the end of the file:





            #using <mscorlib.dll>

            #using <System.Windows.Forms.dll>

            #using <System.dll>

            using namespace System;

            using namespace System::Windows::Forms;

            using namespace System::IO;

            using namespace System::Security::Cryptography;

            #undef MessageBox

          4. Open the dialog template resource and add the controls as you see them in Figure 4-5. (Note that the control on the bottom�below the "Status:" static text�is a list box control.)



            Figure 4-5. Dialog resource for the DESDemo demo application


          5. For the larger edit control (below the "Result:" static text), set the ReadOnly property to True and the AutoHScroll property to False.

          6. Create the DDX member variables as shown in Table 4-3.

          7. Define the following member variable in the CDESDemoDlg class.





            class CDESDemoDlg : public CDialog

            {

            ...

            CString m_strWorkingDir;



            Table 4-3. DDX Variables for the DESDemo Demo

            Control

            Variable Type

            Variable Name

            File name edit

            CString

            m_strFileName

            File name edit

            CEdit

            m_edtFileName

            Result edit

            CString

            m_strFileContents

            Status list box

            CList box

            m_lbxStatus

          8. Add the following code to the OnInitDialog function just before the return statement.





            BOOL CDESDemoDlg::OnInitDialog()

            {

            ...

            TCHAR buff[MAX_PATH];

            GetModuleFileName(NULL, buff, MAX_PATH);

            m_strWorkingDir = System::IO::Path::GetDirectoryName(buff);

            return TRUE; // return TRUE unless you set the focus to a control

            }

          9. At this point, code the button with the ellipsis ("...") on it. This will allow users to use the Windows File Open dialog box to locate their files to encrypt and decrypt:





            void CDESDemoDlg::OnBnClickedFindFile()

            {

            UpdateData();



            CFileDialog dlg(TRUE);

            dlg.m_ofn.lpstrInitialDir = m_strWorkingDir;

            dlg.m_ofn.lpstrFilter =

            _T("Text Files (*.txt)\0*.txt\0"

            "Encrypted Files (*.enc)\0*.enc\0"

            "Decrypted Files (*.dec)\0*.dec\0"

            "All Files (*.*)\0*.*\0"

            );



            if (IDOK == dlg.DoModal())

            {

            // Update the dialog with the selected file.

            m_strFileName = dlg.GetPathName();

            UpdateData(FALSE);



            // Common trick to move the cursor to the end

            // of the edit control so that the user can

            // see the file name in a long name.

            m_edtFileName.SetSel(m_strFileName.GetLength(),

            m_strFileName.GetLength(),

            FALSE);

            }

            }

          10. Insert the EncryptFile and DecryptFile functions from Listings 4-1 and 4-2, respectively. These functions have been intentionally coded in such a way that they can be dropped into any mixed mode (MFC/MC++) application.

          11. Now it's time to use these functions. First add a handler for the Encrypt File button and code it as follows. In a full-production system, you would want to add more error-handling and centralize things like the hard-coded extensions I've chosen for this application (.enc and .dec for encrypted and decrypted files, respectively). However, I've tried to keep things as simple as possible here while still providing a practical, usable demo.



            As you can see, this function's main purpose in life is to determine the output file name (the original file is never overwritten) from the input file name supplied by the user, to call the EncryptFile function, and, upon return, to open and display the encrypted file. In addition, status messages are inserted into the list box in reverse order.



            Note that the input file name is automatically changed to the output name, as the next logical step for the user might be to decrypt the newly created file to test the encryption/decryption round trip.





            void CDESDemoDlg::OnBnClickedEncryptFile()

            {

            #pragma push_macro("new")

            #undef new

            CWaitCursor wc;





            UpdateData();

            if (0 < m_strFileName.GetLength())

            {

            FileStream* pFS;

            try

            {

            CString strStatus;

            m_strFileContents = _T("");



            // If extension ends in .enc, replace with .dec

            // to prevent continual enc/dec of same file resulting

            // in names like file.txt.enc.dec.enc.dec...

            // Otherwise just add .dec

            CString strOutputFile = m_strFileName;

            if (0 == strOutputFile.Right(4).CompareNoCase(

            _T(".dec")))

            {

            strOutputFile =

            strOutputFile.Left(strOutputFile.GetLength() - 4)

            + _T(".enc");

            }

            else

            {

            strOutputFile += _T(".enc");

            }



            // Call EncryptFile function passing just-formatted

            // output name

            strStatus.Format(_T("Encrypting file %s"),

            m_strFileName);

            EncryptFile(m_strFileName, strOutputFile);



            strStatus.Format(_T("%s successfully encrypted"

            "to file %s"),

            m_strFileName,

            strOutputFile);

            m_lbxStatus.InsertString(0, strStatus);



            // Using a FileStream open, read and display newly

            // encrypted file in hex format.

            m_lbxStatus.InsertString(0, _T("Attempting to"

            "read newly encrypted file"));



            pFS = new FileStream(strOutputFile,

            FileMode::Open,

            FileAccess::Read);



            if (pFS->CanRead)

            {

            Byte buffer __gc[] = new Byte __gc[pFS->Length];

            pFS->Read(buffer, 0, buffer->Length);



            for (int i = 0; i < buffer->Length; i++)

            {

            CString c;

            c.Format(_T("%02x "), buffer[i]);

            m_strFileContents += c;

            }

            m_lbxStatus.InsertString(0, _T("Data read back"

            "and displayed"));

            m_strFileName = strOutputFile;

            }

            }

            catch(Exception* e)

            {

            m_lbxStatus.InsertString(0, (CString)e->Message);

            }

            __finally

            {

            pFS->Close();

            }



            UpdateData(FALSE);

            }

            else

            {

            MessageBox::Show(S"You must supply an input file name.");

            }

            #pragma pop_macro("new")

            }

          12. Now, add a handler for the Decrypt File button and code it as follows. Much like the reciprocal Encrypt File button handler, this function's tasks are to determine the output file name from the input file name supplied by the user (so that the original file is not lost), call the DecryptFile function and, upon returning, open and display the decrypted file. In addition, status messages are inserted into the list box in reverse order.





            void CDESDemoDlg::OnBnClickedDecryptFile()

            {

            #pragma push_macro("new")

            #undef new

            CWaitCursor wc;



            UpdateData();

            if (0 < m_strFileName.GetLength())

            {

            StreamReader* pSR;

            try

            {

            CString strStatus;

            m_strFileContents = _T("");



            // If extension ends in .enc, replace with .dec

            // to prevent continual enc/dec of same file resulting

            // in names like file.txt.enc.dec.enc.dec...

            // Otherwise just add .dec

            CString strOutputFile = m_strFileName;

            if (0 == strOutputFile.Right(4).CompareNoCase(

            _T(".enc")))

            {

            strOutputFile =

            strOutputFile.Left(strOutputFile.GetLength() - 4)

            + _T(".dec");

            }

            else

            {

            strOutputFile += _T(".dec");

            }



            // Call DecryptFile function passing just-formatted

            // output name

            strStatus.Format(_T("Decrypting file %s"),

            m_strFileName);

            DecryptFile(m_strFileName, strOutputFile);



            strStatus.Format(_T("%s successfully decrypted to file

            %s"),

            m_strFileName,

            strOutputFile);

            m_lbxStatus.InsertString(0, strStatus);



            m_lbxStatus.InsertString(0, _T("Attempting to read "

            "newly decrypted file"));





            // Using a StreamReader - open, read and display

            // newly decrypted file.

            pSR = new StreamReader(strOutputFile);



            CString strCurrLine;



            while (0 < pSR->Peek())

            {

            strCurrLine = pSR->ReadLine();

            m_strFileContents += strCurrLine;

            m_strFileContents += _T("\r\n");

            }



            m_lbxStatus.InsertString(0, _T("Data read back"

            "and displayed"));

            m_strFileName = strOutputFile;

            }

            catch(Exception* e)

            {

            m_lbxStatus.InsertString(0, (CString)e->Message);

            }

            __finally

            {

            pSR->Close();

            }

            UpdateData(FALSE);

            }

            else

            {

            MessageBox::Show(S"You must supply an input file name.");

            }

            #pragma pop_macro("new")

            }

          13. That's all there is to writing a simple encryption/decryption file using these powerful classes. Now run the application and supply an input file name to encrypt. Once you've done that, decrypt the same file to see the original file's contents. You can use the status list box in case of a problem�for example, the specified input file does not exist or you don't have write privileges for the output file. Your results should be similar to those shown in Figures 4-6 and 4-7.



            Figure 4-6. Example of encrypting a file using the CD's DESDemo demo




            Figure 4-7. Example of decrypting an encrypted file using the CD's DESDemo demo




          Now that you've seen how private key encryption works, let's have a look at public key encryption.













             < Day Day Up > 



            Section 22.1. Performance Characteristics of Stored Programs










            22.1. Performance Characteristics of Stored Programs


            MySQL stored programs can often add to application functionality and developer efficiency, and there are certainly many cases where the use of a procedural language such as the MySQL stored program language can do things that a nonprocedural language like SQL cannot. There are also a number of reasons why a MySQL stored program approach may offer performance improvements over a traditional SQL approach:




            It provides a procedural approach


            SQL is a declarative, nonprocedural language: this means that in SQL you don't specify how to retrieve datayou only specify the data that you want to retrieve (or change). It's up to MySQL itselfspecifically, the MySQL query optimizerto determine how to go about identifying the result set.


            From time to time, we might have a very good idea about the most efficient way to retrieve the data, but find that the MySQL optimizer chooses anotherless efficientpath.


            When we think we know how the data should be retrieved but can't get the optimizer to play ball, we can sometimes use MySQL stored programs to force the desired approach.




            It reduces client-server traffic



            In a traditional SQL-based application, SQL statements and data flow back and forth between the client and the server. This traffic can cause delays even when both the client and the server programs are on the same machine. If the client and server are on different machines, then the overhead is even higher.


            We can use MySQL stored programs to eliminate much of this overhead, particularly when we need to execute a series of related SQL statements. A succinct message is sent from the client to the server (the stored program execution request) and a minimal response is sent from the server to the client (perhaps only a return code). Furthermore, we can take advantage of database triggers to automatically execute statements in the database without any network interaction at all.


            The resulting reduction in network traffic can significantly enhance performance.




            It allows us to divide and conquer complex statements


            As SQL statements become more complex, they also get harder and harder to fully optimize, both for the MySQL optimizer and for the programmer. We have all seen (and some of us have written) massive SQL statements with multiple subqueries, UNION operations, and complex joins. Tuning these "monster" SQL statements can be next to impossible for both humans and software optimizers.


            It's often a winning strategy to break these massive SQL statements into smaller individual statements and optimize each individually. For instance, subqueries could be run outside of the SQL statement and the results forwarded to subsequent steps as query parameters or through temporary tables.



            Having said that, we don't want to give you the impression that we think you should rewrite all of your non-trivial SQL statements in MySQL stored programs. In fact, it is usually the case that if you can express your needs in "straight" SQL, that will be the most efficient approach. And do remember that complex arithmetic computations will usually be slower in a stored program than in an equivalent SQL statement.












            Section 17.3. C API Datatypes







            17.3. C API Datatypes

            Here is a list of C API data types from the
            mysql.h header file:


            MYSQL

            A database handle structure created by
            mysql_init⁠(⁠ ⁠ ⁠) and released with
            mysql_close⁠(⁠ ⁠ ⁠).


            MYSQL_RES

            A structure for a results set from an SQL query. This
            structure is used by fetch functions and is released with
            mysql_free_result⁠(⁠ ⁠ ⁠).


            MYSQL_ROW

            A structure for holding a row of data from a results set. The
            data is retrieved from this structure by the
            mysql_fetch_row⁠(⁠ ⁠ ⁠) function.


            MYSQL_FIELD

            A structure for holding an array of information about a field
            of a results set. The array may be set with the
            mysql_fetch_field⁠(⁠ ⁠ ⁠) function. The elements
            include name, table, and
            def for the default value.


            MYSQL_FIELD_OFFSET

            Used for recording a pointer location for a results set. The
            offset value can be retrieved by the
            mysql_row_tell⁠(⁠ ⁠ ⁠) function and deployed with
            mysql_row_seek⁠(⁠ ⁠ ⁠).


            my_ulonglong

            A variable type for storing the number of rows for functions
            such as mysql_affected_rows⁠(⁠ ⁠ ⁠),
            mysql_num_rows⁠(⁠ ⁠ ⁠), and
            mysql_insert_id⁠(⁠ ⁠ ⁠). To print the value of a
            variable using this type, copy the value to another variable that
            uses the unsigned long type.








            Chapter 7. I18N Actions








             

             










            Chapter 7. I18N Actions





            Topics in This Chapter





            • Overview



            • I18N and L10N



            • Localization Contexts



            • An Overview of the I18N Actions



            • Use of <fmt:message>



            • Request EncodingRequest Encoding



            • I18N Custom Actions







            At the end of the 20th century, with the World Wide Web in its infancy, most Web sites were implemented for a single language, but that's starting to change as more Web sites offer content in multiple languages. Web sites that adapt to a reader's native language and customs have an obvious competitive advantage over those that do not.



            JSTL provides a number of actions that help you internationalize your Web sites:





            • <fmt:setLocale>



            • <fmt:setBundle>



            • <fmt:bundle>



            • <fmt:message>



            • <fmt:param>



            • <fmt:requestEncoding>





            JSTL also provides a handful of actions for formatting and parsing numbers, currencies, percents, and dates in a locale-dependent manner; those actions are discussed in "Formatting Actions" on page 308.



            This chapter begins with an overview of the first five actions listed above, followed by an introduction to internationalization concepts, including locales, resource bundles, Unicode, and charsets. Subsequently, we discuss how you can make the most of the JSTL actions listed above.



            If you don't know anything about the JSTL internationalization (I18N) actions or if you need to brush up on internationalization, you should read this entire chapter. If you're already familiar with the <fmt:message> action but you're not entirely comfortable with locales, resource bundles, charsets and Unicode, you can probably start reading at "I18N and L10N" on page 258.[1] If you've already used the <fmt:message> action and you understand the basics of internationalization, you can begin reading at "Localization Contexts" on page 263.



            [1] The abbreviations I18N and L10N represent the first and last characters and the number of characters in between the words internationalization and localization, respectively.














               

               


              Chapter 6: Distinguished Names and Certificates















































              Chapter 6: Distinguished Names and Certificates




              Overview



              Asymmetric
              encryption provides ways of allowing you to distribute keys with
              relative safety that other people can use to send encrypted messages or
              verify signatures you have created. The problem, however, is that from
              the point of view of the people you are distributing the public keys
              to, the simple presence of a public key is not enough for someone to
              determine whether it is the public key they have or even if the use
              they are being asked to put it to is one that you intended.


              Distinguished names and the certificates that carry them were created to solve this problem.


              This chapter introduces distinguished names,
              certificates, and certification requests. Distinguished names contain
              information about the owner of a public key carried by a certificate.
              Certification requests provide a mechanism by which you can ask some
              other party, presumably trusted by the people you want to give the
              certificate to, to issue you with a certificate that can also be
              trusted. In general, this is done by issuing a certificate that can be
              verified using another certificate issued by the trusted party that is
              already in the hands of the people who you want to accept your new
              certificate.


              By the end of this chapter, you should




              • Understand what an X.500 name is




              • Understand what a public key certificate is, most particularly those that use X.509




              • Be able to make use of the Java classes representing X.500 name and certificates




              • Be able to generate your own certification requests and certificates




              • Be able to create a certificate from a certificate request




              • Be able to form multiple certificates into a certificate chain, or path




              Finally, you should understand how to make use of
              the certificate storage class in Java and how to selectively retrieve
              certificates from it.