1. Trang chủ
  2. » Công Nghệ Thông Tin

Building Oracle XML Applications phần 9 potx

89 189 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Building Oracle XML Applications phần 9 potx
Định dạng
Số trang 89
Dung lượng 608,16 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

In order to turn the static template into a page that dynamically formats production information from our database, we need to perform two basic steps: • Build an XSQL page to assemble

Trang 1

and a string value for a urlArgs variable of:

company=at & t / mobilcom

which contains some spaces, an ampersand, and a slash To test this example transformation, you can use any XML file as the source and try the following:

• With James Clark's xt XSLT 1.0 processor:

$ xt anyfile.xml PortableURLEncoderTest.xsl

• With Michael Kay's Saxon XSLT 1.0 processor:

$ saxon anyfile.xml PortableURLEncoderTest.xsl

• With the Oracle XSLT 1.0 Processor:

$ oraxsl anyfile.xml PortableURLEncoderTest.xsl

All three processors produce the following identical output:

http://foo.com/SomeService?company=at%20%26%20t%20%2F%20mobilecom

We can write a stylesheet that refers to extension functions in several different namespaces (only one of which we're expecting to be usable by the current processor at transformation time) because an XSLT 1.0-compliant processor

signals errors only if you actually try to use the function By wrapping the

extension function in a named template that uses <xsl:choose> and

function-available( ) to selectively invoke the correct function, we can avoid the situation of ever invoking a function that is not available

Trang 2

16.3.4 Debugging Your XSLT Java Extension Functions

You will certainly need to debug your XSLT Java extension functions to make sure they are working correctly Using JDeveloper 3.1, you have two choices for how

to do this, both straightforward

If you are using XSLT in combination with XSQL Pages, you can follow the steps

we outlined earlier for debugging XSQL page action handlers If you set

breakpoints in the Java code for your own XSLT extension functions, the

debugger will stop at your breakpoint when your function is invoked by the XSLT processor

If you want to debug from the command line, you can use JDeveloper 3.1 remote debugging support to debug the command-line Oracle XSLT processor In order

to remotely debug the oraxsl command-line utility, do the following:

• For the simplest possible experience, make sure you run the oraxsl

command-line utility using the Java 1.2 VM supplied with JDeveloper 3.1 This VM natively supports remote debugging on Windows NT, where you'll

be using JDeveloper 3.1 If you've installed JDeveloper 3.1 in the

C:\jdev31 directory, for example, the complete path to the Java VM is C:\jdev31\java1.2\bin\java.exe

• Run the oraxsl command-line utility using the remote debugging

argument to the Java VM If you are trying to test the transformation of

source.xml by the style.xsl stylesheet that makes use of your extension

functions, the exact command will be:

java -XXdebug oracle.xml.parser.v2.oraxsl source.xml style.xsl

If you run this command with the JDeveloper 3.1 Java VM, you will

immediately see the following message on the console:

*** Port is 4000 ***

*** Waiting for debugger connection ***

At this point, the oraxsl program is halted and will not proceed to execute until you attach the JDeveloper 3.1 debugger to it

• In your JDeveloper 3.1 project where the source code for your Java XSLT extension functions resides, make sure that your project is set up for

Trang 3

remote debugging To check this setting, select Project Project

Properties from the main menu, click the Run/Debug tab, and set the Debug As pop-up list to "Remote Debugging"

• Set your desired breakpoints in your extension function source code

• Click the Debug icon in the toolbar

When the Remote Debugging dialog appears, enter localhost for the

machine name and 4000 for the port, then click Attach

When the debugger attaches to the oraxsl process, program execution begins, and you'll hit your breakpoints as soon as the XSLT processor invokes your extension functions

Trang 4

Chapter 17 XSLT-Powered Portals and

Applications

In this last chapter, we'll work our way through all of the important capabilities of XSLT in the context of building some interesting Oracle XML applications By the end of this chapter, I predict you will be an incurable XSLT fanatic as its amazing flexibility transforms you into a more productive developer

17.1 XSLT-Powered Web Store

In this section, we'll build a simple site for a web store that offers the basic functionality illustrated

in Figure 17.1

Figure 17.1 Page map of the Everything.com web site

Visitors to the site will be able to see featured items on the home page and can click the name of one of our "shops" to see a home page specific to that shop They can search the site for a product, and whenever a product appears, they can click a link to see other products by the same

manufacturer

17.1.1 Turning HTML Mockup into an XSLT stylesheet

All product information needs to be displayed consistently across the web store Our web design team has provided us with an HTML mockup, shown in Figure 17.2, of how each product should look on the site The HTML file for the mockup is shown in Example 17.1

Trang 5

Figure 17.2 Mockup of product display for our web store

Example 17.1 Source for HTML Mockup of Product Information

Trang 6

tidy -asxml -indent ProductMockup.html > Product.xsl

The tidy command produces a well-formed XML document in the Product.xsl file, shown in

Example 17.2, which we can use as a starting point for the XSLT transformation

Example 17.2 Tidied HTML Mockup in XML

Trang 7

We can easily turn this tidied-up HTML into an XSLT stylesheet that uses a single root template

We just need to wrap the entire content of the document by:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <! For best results with HTML, best not to introduce any extra whitespace > <xsl:output method="html" indent="no"/>

<xsl:template match="/">

<! Tidied-up HTML document goes here >

</xsl:template>

</xsl:stylesheet>

We don't need the <!DOCTYPE> and/or <META> for our purposes, so we just delete it At this point

we have a valid, functional XSLT stylesheet that will output the static HTML mockup In order to turn the static template into a page that dynamically formats production information from our

database, we need to perform two basic steps:

• Build an XSQL page to assemble the dynamic XML data page with all necessary production information

• Replace the static sample information in the XSLT stylesheet with XSLT actions to plug our dynamic XML into the template HTML format

First things first; we'll start by building the XSQL page

17.1.2 Building an XSQL Page to Assemble Data

Figure 17.3 illustrates our database schema related to products on the web store

Figure 17.3 Database tables involved in the web store

We create the following Store.xsql page to query the product information from the item, author,

maker, and shop tables, using an outer join for the optional author information Note that we've

planned ahead and already associated the Product.xsl stylesheet we're building to format

products for the Web

<?xml version="1.0"?>

Trang 8

<?xml-stylesheet type="text/xsl" href="Product.xsl"?>

<xsql:query max-rows="1" sid="1" connection="xmlbook"

FROM item, author, maker, shop

WHERE item.author_id = author.id (+)

AND item.maker_id = maker.id

AND item.shop_id = shop.id

AND shop.id = {@sid}

</xsql:query>

Since we'll just be testing the page initially, we add the following two additional attributes on the

<xsql:query> action element:

Trang 9

<SKU>0140060898</SKU>

</ROW>

</ROWSET>

With the basic data in place, we turn our attention to evolving the tidied-up HTML mockup, which

is now an XSLT stylesheet, into a dynamic data display

17.1.3 Plugging Dynamic Data into the Stylesheet

We want to design the stylesheet to handle any number of rows of products that might be produced by the query over the product information For each <ROW> element in the <ROWSET>, we want to create an HTML table row (<tr>) containing the product formatted in the standard way

So we introduce an <xsl:for-each> element to wrap the repeating HTML table row, with a

select="ROWSET/ROW" pattern to select all rows in the dynamic XSQL data page The contents of the <xsl:for-each> element will be instantiated in the resulting page for each ROWSET/ROW node selected

Inside the <xsl:for-each> loop, the selected <ROW> element is the current node, so any XPath expressions needed to refer to data in the row can use relative patterns In particular, we do the following, using relative XPath expressions:

1 Replace the static product description:

2 <b>Java in a Nutshell : A Desktop Quick Reference</b>

by David Flanagan

with the dynamic expression:

<b><xsl:value-of select="DESCRIPTION"/></b>

by <xsl:value-of select="AUTHOR_NAME"/>

3 Replace the static text and hyperlink href value in:

Other products by <a href="xxxx">O'Reilly</a>

we'll build later showing all products for a given maker ID

4 Replace the static src attribute's URL:

Trang 10

You Save: <b><xsl:value-of select="YOUSAVE"/></b>

The resulting XSLT stylesheet is shown in Example 17.3

Example 17.3 Stylesheet to Format Web Store Product Information

<! Product.xsl: Format Web Store product information >

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <! For best results with HTML, best not to introduce extra whitespace > <xsl:output method="html" indent="no"/>

Trang 12

This looks just like what the web designers wanted

17.1.4 Formatting Numbers and Conditional Formatting

If we request the Store.xsql page in our browser, passing a value of 4 for the sid parameter to test out an example product in our Toys shop:

http://server/Store.xsql?sid=4

we get the result shown in Figure 17.5

Figure 17.5 Same stylesheet formatting product from the

Toys shop

We immediately notice a few problems:

• The word "by" appears all alone after the product description because toys don't have authors recorded in our database

• The "List Price" and "You Save" sections come up blank since we apparently don't record

a list_price for toys

• The $6.00 price for the Cymbal Clapping Monkey formats as 6 instead of 6.00

Let's temporarily turn off the stylesheet processing by adding an xml-stylesheet=none to the end of the URL:

Trang 13

<?xml-stylesheet type="text/xsl" href="Product.xsl"?>

<xsql:query max-rows="1" sid="1" connection="xmlbook"

FROM item, author, maker, shop

WHERE item.author_id = author.id (+)

AND item.maker_id = maker.id

AND item.shop_id = shop.id

AND shop.id = {@sid}

</xsql:query>

The other two problems indicate a need to conditionally format information We want to display the author name if there is one and handle the "List Price" and the corresponding "You Save" sections similarly We can use the <xsl:if> action to wrap portions of the stylesheet template that need to be conditionally included The <xsl:if> element has a required test="expression"attribute that indicates an XPath expression to be evaluated If the expression tests true, then the content of the <xsl:if> is instantiated in the result tree Otherwise, it is left out

While any XPath expression is valid to include in an <xsl:if> element's test attribute, one of the most common expressions used is a pattern to match an element or attribute If the pattern matches at least one node, the test evaluates to true If no matches are selected by the pattern, the test evaluates to false For example, to test whether an <AUTHOR_NAME> child element exists for the current node, you can use:

Trang 14

<xsl:if test="AUTHOR_NAME">

This returns true if an <AUTHOR_NAME> child element exists and false otherwise If you need to check that an element exists and that it has a non-empty value, you can use a test like:

<xsl:if test="AUTHOR_NAME != ''">

We wrap the display of <AUTHOR_NAME>, <LIST_PRICE>, and <YOUSAVE> elements with

appropriate <xsl:if> tests to produce the Product.xsl stylesheet in Example 17.4

Example 17.4 Stylesheet to Correctly Format All Web Store Products

<! Product.xsl: Format Web Store product information >

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <! For best results with HTML, best not to introduce extra whitespace > <xsl:output method="html" indent="no"/>

Trang 15

<b>Our Price: <font color="blue">

Figure 17.6 Formatting price and handling missing author

and list price

Going back to our numerical formatting, when using XSLT to format database query results, there are two strategies for formatting numbers:

• Use the database TO_CHAR( ) function in your SELECT statement to format the number using SQL format masks

• Use the XSLT format-number( ) function

Trang 16

The first strategy is easier if you're already familiar with the SQL TO_CHAR( ) function and its format mask language The latter is better if you need to work with the numerical value of an element within the stylesheet for calculations For example, if your query includes

TO_CHAR(SAL,'$9,999.00') ASSALARY in its SELECT list, the corresponding

<SALARY>$2,450.00</SALARY> will work fine if you are simply including its value verbatim into the transformed result However, if your stylesheet needs to test if the expression SALARY > 4500

is true, or needs to use the value of <SALARY> in a calculated expression, having a <SALARY>value from the database that includes a dollar sign and commas will cause problems

The XSLT format-number( ) function takes arguments for the number and the format mask, but

it uses the format masks supported by the java.text.DecimalFormat class in the JDK While other pattern characters are supported, the four key pattern characters of immediate interest are:

Placeholder for a group separator

Any character, such as a dollar sign, can appear at the start or the end of a format mask To format our <LIST_PRICE> element using format-number( ) we would use the syntax:

<xsl:value-of select="format-number(LIST_PRICE,'0.00')"/>

17.1.5 Handling Raw HTML/XML from Database Columns

For some products in the Electronics shop, vendors supply us with a short product blurb in HTML that highlights the key features of the product An example blurb looks like this:

<ul>

<li>AM/FM and shortwave world-time travel clock radio</li>

<li>11-band travel radio (AM, FM, SW 1-9)</li>

<li>LED tuning indicator</li>

<li>Built-in alarm with snooze button</li>

Trang 17

<li>Compact design and protective lid</li>

</ul>

We can easily add the BLURB column to the SELECT list in our Store.xsql page, and add another

conditional section in our stylesheet:

we'll see a "Features" section in the browser like that shown in Figure 17.7

Figure 17.7 Display of HTML markup escaped by XSLT

processor

Yikes! This occurs because an XSLT processor is required to escape all illegal characters like angle brackets and ampersands when they occur in text values, so the browser displays the literal angle bracket characters and ampersands as text We want these literal characters to appear verbatim and unescaped in the output of the transform so the browser will interpret them as HTML tags and not display them as literal angle brackets For these cases, XSLT supplies the optional

disable-output-escaping="yes" attribute on <xsl:value-of> or <xsl:text> This instructs the processor to disregard the normal escaping mechanism and allows the literal HTML or XML markup to pass through verbatim to the result document So if we rewrite our BLURB section of the stylesheet to look like this:

Trang 18

</xsl:if>

then the products in our Electronics shop will now display correctly, as shown in Figure 17.8

Figure 17.8 Correctly displaying HTML-based product blurb

Serving raw HTML fragments out of database columns directly into web pages can be very useful when you control and trust the content However, if you are serving information from the database that end users might have entered, there are

potential security risks in serving this unverified HTML markup back into the browser The risks a developer must be aware of are documented at

such occurrences are pretty rare, forewarned is forearmed

Now that we've established the basic product formatting, we need to build these four web site pages:

• Featured Items page

• Search Results page

• Items by Maker page

• Shop Home page

We'll build the four XSQL pages shown in Figure 17.9, leaving StoreTop.xsql for later

Trang 19

Figure 17.9 Map of XSQL pages to implement the web store

Basically, these pages differ only in the WHERE clauses of their queries The query in the

FeaturedItems.xsql page needs to look like this:

FROM item, author, maker, shop

WHERE item.author_id = author.id (+)

AND item.maker_id = maker.id

AND item.shop_id = shop.id

AND item.id IN ( SELECT itemid

SearchResults.xsql page looks about the same, but has the following WHERE clause:

WHERE item.author_id = author.id (+)

AND item.maker_id = maker.id

Trang 20

AND item.shop_id = shop.id

AND UPPER(item.description) LIKE UPPER('%{@find}%')

The same goes for the Maker.xsql page, which shows additional products from the same maker as

another product Its WHERE clause looks like this:

WHERE item.author_id = author.id (+)

AND item.maker_id = maker.id

AND item.shop_id = shop.id

AND maker.id = {@id}

For the ShopHomePage.xsql, we'll use a CURSOR expression to retrieve in a single query the

name of the shop, given the shop ID that will be passed in as a parameter as well as the nested set of available products:

FROM item, author, maker

WHERE item.author_id = author.id (+)

AND item.maker_id = maker.id

AND item.shop_id = shop.id

ORDER BY shop_name, maker_name

17.1.6 Factoring Reusable Transformation Routines

Finally, we attack the XSLT stylesheets for the four key product pages All of these pages will be using different queries to display products We can take advantage of XSLT named templates to

Trang 21

factor the product display part of the template we created earlier into a subroutine to be

leveraged by multiple stylesheets After adding another conditional section to avoid displaying

"Other products by " on a page that does not include a MAKER_ID, we end up with the named

displayProduct template in Example 17.5

Example 17.5 Named Template to Display Web Store Products

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template name="displayProduct">

Trang 22

the FeaturedItems.xsl stylesheet, which includes DisplayProduct.xsl, then uses

<xsl:call-template> to invoke the displayProduct template inside its <xsl:for-each> loop

to format the current <ROW> of product information

Example 17.6 Calling Reusable Formatting with Named Templates

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <! Include the stylesheet containing the "displayProducts" template >

<xsl:include href="DisplayProduct.xsl"/>

<! Set the output to not indent so no extra whitespace is introduced >

<xsl:output method="html" indent="no"/>

Trang 23

To build the title frame for the site, we can encapsulate the structure of the store in an XSQL page

containing just static XML elements, as shown in StoreTop.xsql Each store and the shops it

contains can be represented by <store> and <shop> elements:

<?xml version="1.0"?>

<?xml-stylesheet type="text/xsl" href="StoreTop.xsl"?>

<store>

<shop name="Outdoors" id="1"/>

<shop name="Electronics" id="3"/>

<shop name="Books" id="2"/>

<shop name="Toys" id="4"/>

</store>

The simple-form XSLT stylesheet in Example 17.7 transforms the store structure into a

nice-looking banner with a search box and hyperlinks to each particular shop in our store

Example 17.7 Simple-Form Stylesheet to Handle Web Store Home Page

<! StoreTop.xsl: format the main homepage of the Web Store >

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <! For best results with HTML, best not to introduce extra whitespace > <xsl:output method="html" indent="no"/>

Trang 24

<table>

<tr>

<td>

<xsl:text>Search the store:</xsl:text>

<input type="text" name="find"/>

By tying the StoreTop.xsql and the FeaturedItems.xsql pages into an HTML frameset with a little

Store.html document like this:

<html>

<head>

<title>XML Book XSQL Sample Site</title>

</head>

<frameset border="no" rows="90,*">

<frame frameborder=no noresize name="top" src="StoreTop.xsql">

<frame frameborder=no name="main" src="FeaturedItems.xsql">

</frameset>

</html>

we end up with the clean, simple site in Figure 17.10 that shows products in a uniform way across the whole site

Trang 25

Figure 17.10 Everything.com home page

17.2 Building a Personalized News Portal

In this section, we'll build our second application—a dynamic, database-driven news portal that derives its content from user preference information

17.2.1 News Category Paging Example

Web-based applications frequently present information to the user in pages Just consider some sites you might be familiar with If you perform a search at Amazon.com, WilliamsSonoma.com,

or Google.com, you will see your search results a page at a time with a little paging widget that shows you where you are As Figure 17.11 illustrates, each site has a slightly different look to its paging widget, but each one offers the same functionality

Trang 26

Figure 17.11 Example paging widgets

All of these sites present the results of querying large, relational databases However, to keep the amount of information on any given page manageable and to keep your response time fast, they all present the results incrementally in pages A naive technique for scrolling through database query results like this is to open a database cursor, fetch ten records, and keep the cursor open

so when the user clicks on the "next page" icon you can fetch ten more pages However, this technique has many drawbacks:

• Generally, database cursors can only be scrolled forward, so offering a facility to go back

to the previous page would be tricky

• Scrolling backwards or maintaining a current record position in a cursor requires keeping the database cursor open across web page requests This means you must dedicate a database connection to every single visitor to your site "Holy

maximum-number-of-sessions-exceeded, Batman!" Twenty thousand users querying for web sites, books, or pots and pans would need 20,000 database connections to keep each

of their cursors open And what if users never click on the "next page" button before zooming off to another site to check their stocks? In this case, when do you close the cursor?

It's clear that we need a technique to deliver the query results in a stateless way, so a user can come in, browse some results, and leave without dedicating a database connection to the results and without having to keep track of when to close the results The stateless paging technique is actually quite simple once you are willing to believe that requerying data you've already queried

is not in violation of some database user's moral code

The trick works like this Say you have a database query which produces a list of 114 books from your database, like this:

SELECT title, description, price, sku, imageurl

FROM big_table_of_products

WHERE author like '%THEROUX%'

Here's how we can orchestrate the stateless paging through the results:

Trang 27

1 Rather than retrieving all of the rows in the result to determine how many total rows we've found, we instead leverage our powerful database to do it for us with a second query like this one:

2 SELECT COUNT(sku)

3 FROM big_table_of_products

WHERE author like '%THEROUX%'

If you're querying and counting columns that are indexed in the database, the answer to this count query can be determined very, very quickly

4 If we are displaying the first page of results, say with ten rows per page, we fetch just ten rows from the original query, show them to the user, and close the cursor

5 Later, if and when we're asked to display page number 2, we use the total number of rows per page to calculate how many rows to skip over, and re-execute the same query we did before This time, to show rows 11-20 we:

a Skip 10 rows by fetching them and throwing them away

b Fetch the 10 rows for 11-20 and present them to the user

c Close the cursor

At first, this skip rows technique sounds slow, but remember that it's the job of the Oracle database engine to keep the most recently accessed data in its buffer caches so that frequently accessed information is accessible super-quickly Our seemingly inefficient skip-and-query technique actually performs much better than the alternative strategy of leaving lots of database cursors open

Stateless Paging

Stateless paging through query results can be made even more efficient

if your query results are ordered by a column value that makes each row

distinct For example, consider a list of employees whose last name is

like `%JONES%' that is sorted by user ID If you remember the value of

userid that was at the bottom of the current page of results—say

`abjones'—you can use Oracle8i 's powerful top N query feature to get

the next page of results without requerying rows you've already seen It

merely requires adjusting the WHERE clause of the query like this:

SELECT userid, firstname, lastname, phone

FROM ( SELECT userid, firstname, lastname, phone

FROM all_emps

WHERE lastname like '%JONES%'

/* This was the orig query critera */

AND userid > 'abjones'

Trang 28

/* Return matches beyond 'abjones' */

This optimizes the "next page" query and avoids the need to query and

skip rows, but makes it difficult to support randomly jumping to page n of

m You can combine the skip-and-query technique with this to achieve a

hybrid technique, however

To implement stateless paging in XSQL Pages-based applications, we can take advantage of the

skip-rows="n" and max-rows="m" attributes on <xsql:query> or

<xsql:ref-cursor-function> to adopt the skip-and-query technique However, since it's clear from the three examples above that the look of the page navigation display can vary from use to use, it's best if we try to abstract the raw information required for paging and separate it from the presentation We'll include the raw paging information in our XSQL page and then use an XSLT template to flexibly format it in any look we want

Given the number of rows per page as an input parameter, the following basic paging information

is required to produce a display like those shown previously:

• The total number of rows retrieved by the query

• The total number of pages that this translates into

• The current page number

• If applicable, the previous page number

• If applicable, the next page number

To make the mechanism generic, we also need to add:

• The URL of the current page

• The list of URL arguments to pass to the current page

We're going to use a custom XSQL action handler to take care of all these details for us, so that given a simple action in our XSQL page:

<xsql:action handler="Paging" rows-per-page="10" url-params="author title">

<! SQL Statement to Use for Calculating the Total Rows >

</xsql:action>

we will automatically find a <paging> information fragment in our data page that will look like this:

<paging>

Trang 29

1 Retrieves the text of the SQL statement to use as a total row count query by calling

4 Calls a private method to determine the name of the current page

5 Calculates the total number of pages as well as the next page and previous page numbers based on the value of the current page, which is reflected by the request parameter named p

6 Constructs the <paging> element and subelements and adds them to the action handler result root

7 Sets the page-private parameters named paging-skip and paging-max to be used by an

<xsql:query> or <xsql:ref-cursor-function> element later in the same XSQL page to control the appropriate numbers of records to skip to retrieve the records for the current page number

Example 17.8 Custom XSQL Action Handler Simplifies Stateless Paging

import oracle.xml.xsql.*;

import oracle.xml.parser.v2.*;

import org.w3c.dom.*;

Trang 30

import java.net.*;

import java.util.StringTokenizer;

import java.sql.SQLException;

public class Paging extends XSQLActionHandlerImpl {

private static final String PAGE_PARAM_NAME = "p";

private static final String ROWSPERPAGE = "rows-per-page";

private static final String TARGETPAGEARGS = "url-params";

public void handleAction(Node root) throws SQLException {

XSQLPageRequest req = getPageRequest( );

Element actElt = getActionElement( );

// Get the count query from the action element content

String query = getActionElementContent( );

// Get the number of rows per page, defaulting to 10

long pageSize = longVal(getAttributeAllowingParam(ROWSPERPAGE,actElt),10); long totalRows = longVal(firstColumnOfFirstRow(root,query),0);

long curPage = longVal(variableValue(PAGE_PARAM_NAME,actElt),1);

// Get the name of the current page to use as the target

String pageName = curPageName(req);

// Get any URL parameter names that need to be echoed into paging URLs

String pageArgs = getAttributeAllowingParam(TARGETPAGEARGS,actElt);

// Calculate the total number of pages

long totalPages = totalRows / pageSize;

long fract = totalRows % pageSize;

if (fract > 0) totalPages++;

// Make sure current page is between 1 and totalPages

if (curPage < 1) curPage = 1;if (curPage > totalPages) curPage = totalPages; // Create the <paging> fragment to add to the "data page"

req.setPageParam("paging-skip",Long.toString((curPage-1)*pageSize));

Trang 31

req.setPageParam("paging-max",Long.toString(pageSize));

}

// Get the name of the current page from the current page's URI

private String curPageName(XSQLPageRequest req) {

String thisPage = req.getSourceDocumentURI( );;

int pos = thisPage.lastIndexOf('/');

if (pos >=0) thisPage = thisPage.substring(pos+1);

private String expandedUrlParams(String paramNameList,Element actElt) {

// Allow comma-separated or space-separated name list

paramNameList = paramNameList.replace(',',' ');

StringTokenizer st = new StringTokenizer(paramNameList);

StringBuffer paramString = new StringBuffer( );

int tokens = 0;

while (st.hasMoreTokens( )) {

String paramName = st.nextToken( );

String paramValue = variableValue(paramName,actElt);

Trang 32

belong on the current page by setting their respective max-rows and skip-rows attributes to the values of the page-private parameters as follows:

skip-rows="{@paging-skip}"

max-rows="{@paging-max}"

The following NewsCategorySimple.xsql example shows how these parameters are used:

<?xml version="1.0"?>

<?xml-stylesheet type="text/xsl" href="NewsCategorySimple.xsl"?>

<page id="1" connection="xmlbook" xmlns:xsql="urn:oracle-xsql">

<xsql:action handler="Paging" rows-per-page="6" url-params="id">

SELECT count(id)

FROM site_newsstory

WHERE category = {@id}

ORDER BY TIMESTAMP DESC

</xsql:action>

<xsql:query skip-rows="{@paging-skip}" max-rows="{@paging-max}">

SELECT title as "Title",

TO_CHAR(timestamp,'Mon DD') as "Date"

FROM latestnews

WHERE category = {@id}

ORDER BY TIMESTAMP DESC

</xsql:query>

</page>

This renders as shown in Figure 17.12

Figure 17.12 News stories with a paging display

If we hover over the Next link, we see that it is:

http://server/NewsCategorySimple.xsql?p=2&id=1

which links to the same XSQL page but passes a p=2 parameter to request "page 2" of the query results id=1 is the current value of the news story category ID that needs to be passed to the

Trang 33

page in order to query stories from the right category This id parameter appears in the URL because we included its name in the url_params attribute in the Paging action handler If we click on the Next link to go to page 2, and look at its raw data page by tacking on an extra

xml-stylesheet=none parameter to the URL, we'll see the data page in Figure 17.13

Figure 17.13 Raw data page for news stories with paging

information

Querying page 2 with its normal set of arguments shows that we get the second page of news stories and a paging display, as shown in Figure 17.14, that correctly reflects "Page 2 of 4" with both Previous and Next links

Trang 34

Figure 17.14 Preview and next links appear when

appropriate

How is this magic happening? As with all XML/XSL-based solutions, the answer is that the magic

is cleanly separated into data magic and presentation magic XSQL pages and our custom Paging

action handler do the data side of the paging magic, which involves:

• Setting up the <paging> fragment in the data page, based on the rows-per-page="6"attribute and the "row count query" included in the <xsql:action> element

• Querying the "window" of rows for the current page by using the

skip-rows="{@paging-skip}" and max-rows="{@paging-max}" attributes on the

<xsql:query> action element

The look and feel of the page is handled by the following simple stylesheet:

Trang 35

</xsl:stylesheet>

Since we're not overriding any templates we're leveraging from other library stylesheets, we can use <xsl:include> to include the templates as if they were part of this one We're including the

templates from our TableBaseWithCSS.xsl stylesheet in Chapter 7, to display the queried rows of

data as a table with column headings, and we're including the UtilPaging.xsl stylesheet in

Example 17.9 to handle the presentation of the paging display Since we used a CSS stylesheet

to separate font and color information from the TableBaseWithCSS.xsl transformation, it's easy to use a different Forum.css stylesheet here to give the page a new font and color scheme

Note that the root template controls the order in which the data and paging information is displayed by explicitly using:

<! Transform the ROWSET of query results first >

of the paging information is displayed This lets us override the entire paging display later by

importing UtilPaging.xsl and providing a new template for <paging> We alternatively can just override the way that the Previous and Next links are rendered by leaving the existing paging template in place and overriding the previousLink and nextLink named templates We might do this, for example, to use fancy previous and next page icon buttons like the earlier Amazon.com example instead of the default Next and Previous HTML links

Trang 36

XSLT supports both <xsl:include> and <xsl:import> for reusing existing stylesheets in other stylesheets that you build Use <xsl:include> if you want to add all the templates from another stylesheet logically into your current one Use

<xsl:import> to include other templates, and set all the templates in the current stylesheet to be considered as more important than the included ones A rule of thumb is that if you are overriding templates from another stylesheet, use

<xsl:import>; otherwise, use <xsl:include> Both

<xsl:include> and <xsl:import> can only be used as top-level elements, that is, as immediate children of the

| UtilPaging.xsl: Transform <paging> structural info into HTML

| presentation for "Page N of M" and Next/Prev Links

Trang 37

<xsl:with-param name="pagenum" select="prev-page"/>

<xsl:with-param name="label" select="$label"/>

<xsl:with-param name="pagenum" select="next-page"/>

<xsl:with-param name="label" select="$label"/>

Trang 38

So we now have:

• A generic facility for adding stateless paging to any XSQL page

• A library stylesheet to handle formatting the "Page N of M " display

We'll put both of these to good use in several of the following examples, as we build our simple news story display into a full-fledged, database-driven information portal

17.2.2 Building Reusable HTML Widgets

Let's learn some cool XSLT tricks to embellish the simple list of news stories presented in Example 17.12 Our web designers have given us an HTML mockup of how they want the news stories on our portal site to look, and have provided us a CSS stylesheet with all their chosen font/color settings to be used across the site The mockup of the news display is shown in Figure 17.15

Figure 17.15 HTML mockup for portal news display

The designers tell us "rounded boxes are in!" and inform us that everything on the portal site should be displayed using cool-looking rounded boxes with the title and content inside the box They have taken the time to figure out the HTML layout and the rounded-corner GIF images required to render the rounded box, so we don't have to figure out that part Since we're going to need this "TitledBox" widget in many places on our site, our first thought should be to design a reusable XSLT template to represent the box So, let's do it

We take the HTML mockup source code and run it through the Tidy utility to turn it into

well-formed XML, and we add the necessary <xsl:stylesheet> around it We want the TitledBox widget to be callable from any place we need it, so we make it a named template We want to be able to pass the title and the box contents to the widget so we can use it to display the rounded box with any title and any content we need inside the box

Here we can make use of an XSLT feature we haven't used yet: template parameters In Chapter

9, we used top-level stylesheet parameters, but both match="pattern" templates and named templates can define template-scoped parameters as well Parameters are given a name and optionally a default value when they are declared at the top of the template, using the following syntax:

Trang 39

<xsl:template name="SomeTemplate">

<! "title" parameter has no default value >

<xsl:param name="title"/>

<!

| "list-of-rows" parameter defaults to list of ROW element children of

| the current node, using the XPath expression ROW

+ >

<xsl:param name="list-of-rows" select="ROW"/>

<! "rowname" parameter defaults to the string 'ROW' (note extra quotes!)

<xsl:param name="rowname" select="'ROW'"/>

<! "otherrowname" parameter defaults to the string 'ROW' as well

A parameter can have any of the supported types XPath expressions can return—String, Number,

Boolean, and NodeSet—or it can be a ResultTreeFragment of literal nodes Using parameters,

we can make the title of our Titled Box, as well as its content, be completely parameterized, so we can reuse the same template to do a cool rounded-corner box with a title anywhere throughout our site

Our TitledBox.xsl stylesheet ends up looking like Example 17.10

Example 17.10 Named Template for a Titled Box with Rounded Corners

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template name="TitledBox">

<xsl:param name="Title"/>

<xsl:param name="Contents"/>

<table width="100%" cellspacing="0" cellpadding="0" border="0">

<tr>

<td width="1%" valign="top" align="left" bgcolor="#CCCC99">

<img src="images/TL.gif" width="5" height="5" />

</td>

<th nowrap="" width="98%" align="left" valign="center" bgcolor="#CCCC99"> <! &#160; is numerical character entity for Non-Breaking Space > <xsl:text>&#160;&#160;</xsl:text>

Trang 40

<! Put whatever is passed as the Title parameter here >

<td width="1%" valign="top" align="right" bgcolor="#CCCC99">

<img src="images/TR.gif" width="5" height="5" />

<td nowrap="" bgcolor="#F7F7E7" colspan="3">

<! Put whatever is passed as the Contents parameter here >

<xsl:copy-of select="$Contents"/>

</td>

</tr>

<tr>

<td bgcolor="#F7F7E7" width="1%" align="LEFT" valign="BOTTOM">

<img src="images/BL.gif" width="5" height="5" border="0" />

</td>

<td colspan="2" bgcolor="#F7F7E7" height="1" width="98%">

<img src="images/blank.gif" border="0" height="1" width="1" />

</td>

<td bgcolor="#F7F7E7" width="1%" align="right" valign="BOTTOM">

<img src="images/BR.gif" width="5" height="5" border="0" />

The template defines the two parameters Title and Contents at the top, and right in the middle

of the HTML table that contains the layout and images to achieve the rounded corner effect for the box, we see:

<! Put whatever is passed as the Title parameter here >

<xsl:copy-of select="$Title"/>

where the title is plugged in and:

Ngày đăng: 08/08/2014, 18:21

TỪ KHÓA LIÊN QUAN