Home of: [Atelier "FUJIGURUMA"] >> [PageMixer hosted by SourceForge.net]

SEE "For Readers of English Version",
or Japanese version of this page

Iterate sub-sequence

This section explains how to iterate part of HTML page in PageMixer framework. For this purpose, this section uses "shopping basket" example and the filter iterate and inset data into part between "<tr>" and corresponding "</tr>" in HTML page.


<table class="basket">
  <tr class="Shop-Basket-Entry">
    <td>
      <span class="Shop-Basket-EntryName">(Name)</span>
    </td>
    <td>
      <span class="Shop-Basket-EntryPrice">(Price)</span>
    </td>
    <td>
      <input type="text" class="Shop-Basket-EntryCount"
                name="xxx" value="0">
    </td>
    <td>
      <span class="Shop-Basket-EntryTotal">(Sub total)</span>
    </td>
  </tr>
</tabl>

Before processing

<table class="basket">
  <tr class="Shop-Basket-Entry">
    <td>
      <span class="Shop-Basket-EntryName">banana</span>
    </td>
    <td>
      <span class="Shop-Basket-EntryPrice">120</span>
    </td>
    <td>
      <input type="text" class="Shop-Basket-EntryCount"
             name="Shop.Basket.Entry.ID:i0001" value="1">
    </td>
    <td>
      <span class="Shop-Basket-EntryTotal">120</span>
    </td>
  </tr>
  <tr class="Shop-Basket-Entry">
    <td>
      <span class="Shop-Basket-EntryName">broccoli</span>
    </td>
    <td>
      <span class="Shop-Basket-EntryPrice">165</span>
    </td>
    <td>
      <input type="text" class="Shop-Basket-EntryCount"
             name="Shop.Basket.Entry.ID:i0002" value="2">
    </td>
    <td>
      <span class="Shop-Basket-EntryTotal">330</span>
    </td>
  </tr>

          :
          :

</table>

After processing

Overview

Class diagram

Class diagram in this section is shown below:

Class diagram
Class diagram (click for large figure)

Classes which you must define are colored, and other are already defined.

Object diagram

Object diagram in this section is shown below:

Object diagram
Object diagram (click for large figure)

Processing flow

Sequence diagrams in this section are shown below:

First diagram is one until filter detects sub-sequence to be iterated.

Sequence diagram - (1)
Sequence diagram - (1) (click for large figure)

And second is one after getting iterator from DataProvider.

Sequence diagram - (2)
Sequence diagram - (2) (click for large figure)

Class names

In this tutorial, abbreviated class names are used. Complete names are shown below.

Classes of PageMixer framework

NotationFull name
ConsumerContext jp.ne.dti.lares.foozy.pagemixer.mixer.ConsumerContext
DataProvider jp.ne.dti.lares.foozy.pagemixer.mixer.DataProvider
HTMLSymbolSet jp.ne.dti.lares.foozy.pagemixer.HTMLSymbolSet
ListDataProvider jp.ne.dti.lares.foozy.pagemixer.mixer.ListDataProvider
SequenceDataIterationFilter jp.ne.dti.lares.foozy.pagemixer.mixer.SequenceDataIterationFilter
SequenceWatcher jp.ne.dti.lares.foozy.pagemixer.mixer.SequenceWatcher

Tutorial specific classes

NotationFull name
BasketEntry common.BasketEntry
BasketEntryIterationFilter pagemixer.filter.BasketEntryIterationFilter
Bootstrap pagemixer.filter.Bootstrap
EntryCountAttr pagemixer.filter.BasketEntryIterationFilter.EntryCountAttr
EntryNameText pagemixer.filter.BasketEntryIterationFilter.EntryNameText
EntryPriceText pagemixer.filter.BasketEntryIterationFilter.EntryPriceText
EntryTotalText pagemixer.filter.BasketEntryIterationFilter.EntryTotalText

Define data class

This section iterates part of HTML page for instance collection of below class.


public class BasketEntry
{
    // "ShoppingItem" contains ID, name, price of item.
    final
    private ShoppingItem item_;

    private int count_;

    ////////////////////////////////////////

    public BasketEntry(ShoppingItem item, int count){
        item_ = item;
        count_ = count;
    }

    ////////////////////////////////////////

    public String getID(){ return item_.getID(); }

    public String getName(){ return item_.getName(); }

    public String getName(Locale locale){
        return item_.getName(locale);
    }

    public int getPrice(){ return item_.getPrice(); }

    public void setCount(int count){ count_ = count; }

    public int getCount(){ return count_; }
}

Class of iterated data

Create DataProvider

To iterate part of HTML page, data collection provider is needed. Given collection can tell how many times it should repeat, and what data is should be inset.

PageMixer framework generalizes what provides collection of data as "DataProvider". It is interface, and hides implementation of data holding.

It defines only one method as below.


public interface  DataProvider
{
    public java.util.Iterator provide();
}

DataProvider

"provide()" returns java.util.Iterator, which is of data collection. You can get Iterator of it by invoking DataProvider#provide always when you need it.

In this example, DataProvider which provides collection of BasketEntry is created as below.


    // "entryList" as java.util.List
    // contains "BasketEntry"s
    ListDataProvider provider =
    new ListDataProvider(entryList);

Create DataProvider

"ListDataProvider" is implementation class of DataProvider with java.util.List.

Of course, you can define "shopping basket" class, which holds BasketEntry objects, as implementation class of interface DataProvider.

NOTE: You do not have to create DataProvider, if you override getIterator(Object) of SequenceDataIterationFilter.

This allow you to get Iterator from your specific object directly.

Define identifiers of data and DataProvider

To identify DataProvider and data to be inset, SequenceDataIterationFilter requires two keys like below:


Object providerKey = "Shop.Basket.BasketEntryProvider";

Object dataKey = "Shop.Basket.BasketEntry";

Identifiers to identify data and DataProvider

"providerKey" identifies DataProvider, and "dataKey" identifies data, which may be BasketEntry object.

Define co-operative filters

In this section, "SequenceDataIterationFilter" is used as main filter, but the only thing which SequenceDataIterationFilter affects to Token sequence directly is to iterate part of Token sub-sequence. It does not inset data into iterated Token sub-sequence by itself.

Instead of doing so, it delegates responsibility to do so to co-operatvie other filters. This separation makes SequenceDataIterationFilter simple and re-usable.

SequenceDataIterationFilter#push(Filter) is used to register co-operative filter, and registered one is used only to inset data into iterated sub-sequence.

Below filters may be needed for "shopping basket" example.

In fact, these filters are defined as inner class of sample filter class in this section. Each of these are so simple and easy to understand with knowledge of former sections that this section omits explanation of them. Please see source files directly for detail.

Create SequenceWatcher

As described above, this section let the filter iterate part between "<tr>" and corresponding "</tr>" in HTML page. But, it is not good idea to repeat all parts between them in given HTML page.

Then, condition of target sub-sequence are:

In this case, "SequenceWatcher.NameAttr" is used as "SequenceWatcher". It is "Sequence Watcher watching at Name and Attribute of first token".


new SequenceWatcher.NameAttr(HTMLSymbolSet.SET.TR,
                             HTMLSymbolSet.SET.CLASS,
                             "Shop-Basket-Entry")

SequenceWatcher to iterate between "tr"

Extend SequenceDataIterationFilter

Sample filter class of this section is defined as below.


public class BasketEntryIterationFilter
    extends SequenceDataIterationFilter
{
    final static
    private HTMLSymbolSet SET = HTMLSymbolSet.SET;

    final static
    private String VALUE = "Shop-Basket-Entry";

    ////////////////////////////////////////

    public BasketEntryIterationFilter(Object providerKey,
                                      Object dataKey)
    {
        super(new SequenceWatcher.NameAttr(SET.TR,
                                           SET.CLASS,
                                           VALUE),
              providerKey,
              dataKey);

        push(new EntryNameText(dataKey));
        push(new EntryPriceText(dataKey));
        push(new EntryCountAttr(dataKey));
        push(new EntryTotalText(dataKey));
    }
}

Extend SequenceDataIterationFilter

Classes of pushed instance in constructor are of co-operative filters.

Connect and mix

Now, everything needed are ready to use. Execution code is as below (see BasketEntryIterationFilter for detail).

try{
    // key to set/get DataProvider
    final Object providerkey =
    "Shop.Basket.BasketEntryProvider";

    // key to set/get provided data
    final Object dataKey =
    "Shop.Basket.BasketEntry";

    Bootstrap bootstrap =
    new Bootstrap.Default(filename)
    {
        protected void prepare(ConsumerContext context)
        {
            List entryList = BasketEntry.getEntryList();

            ListDataProvider provider =
            new ListDataProvider(entryList);

            // put DataProvider into context
            context.setValue(providerKey, provider);
        }
    };

    BasketEntryIterationFilter filter =
    new BasketEntryIterationFilter(providerKey, dataKey);

    // apply the filter
    bootstrap.execute(filter);
}
catch(Exception e){
    e.printStackTrace(System.err);
}
Connect and Mix

Sample HTML file as input is "basket.en.html" under "src/demo/servlet/war/WEB-INF/page/demosite" in distribution.

You can see some(determined by random number) entries of "shopping basket" in result HTML page of above execution code.


To next section "Combine filters"