org.makumba.list.tags |
org.makumba.view.jsptaglib
Implements the JSP Tag Library to use makumba in JSP pages.
The Makumba JSP rendering engine
See also a graphical description of the terms below.
The Makumba JSP rendering engine runs the algorithmic process of
converting mak:list, mak:object, mak:value, mak:input to OQL queries and going
through their results to display the data in pages . The rendering
engine must overcome a wealth of deficinencies of the JSP standard. The
JSP standard offers limited capabilities in regard to:
- page identification: it is impossible to know which page has a
tag been declared in. It is possible to find out which page has been
requested, but if that page includes other pages, there is no way to
find out if the tag is in an included file or not. The tomcat-dependent
class {@link org.makumba.view.jsptaglib.TomcatJsp} solves the problem
- page analysis: JSP provides a way to get access to an XML image
of the JSP file, but whatever analysis is done there is not accessible
to the tags at page runtime
- tag identification: the
javax.servlet.jsp.tagext.Tag class has no way of identifying
itself (e.g. it knows that "I'm a mak:list tag" but it does not know "I
am the mak:list query from file F, line C, column L, my constant
attributes are as follows: from=... where=...., I contain the
following mak:list tags.... and the following mak:value tags:...., my
mak:list parent is ...., therefore my query will be: .... ". This
problem is solved by parsing the JSP page in classes like {@link
org.makumba.util.JspParseData} and {@link
org.makumba.view.jsptaglib.MakumbaJspAnalyzer }
- communication between instances of the same tag: if two javax.server.jsp.tagext.Tag
represent the same physical tag executing at different times within the
same request, it is impossible for the first to leave some data for the
other to pick up on and continue
Terms used for taglib implementation
To explain in more detail how the above problems are solved, we will
resort to a glossary of terms, which should be referred from the
javadocs of the classes in this package.
- iteration: an iteration
of a mak:list (or mak:object, below all references to mak:list also stand for mak:object)
- parentList: below, A is
the parent mak:list of
B (same goes for any combination of mak:list and mak:object)
<mak:list A > ...<some:otherTagThatIsNotMakListOrObject C > ... <mak:list B> ... </mak:list> ...</some:otherTagThatIsNotMakListOrObject> ... </mak:list>
- rootList: a mak:list that has no parentList
- parentIteration: an
iteration of the parentList of
the mak:list.
parentIterations are
important because the object that represents the mak:list (of type {@link
org.makumba.view.jsptaglib.QueryTag} ) changes between one parent
iteration and another. Due to that, makumba must keep somewhere the data
iterated by a mak:list, and
retrieve it at each parentIteration.
- iterationGroup: all
iterations of a mak:list that
are done within the same parentIteration
(by the same {@link org.makumba.view.jsptaglib.QueryTag})
- listQuery: every mak:list from the page produces a
query.
The query is executed only once (if not repeated via some scriplet), no
matter how many parent iterations
there are.
- parentQuery: queries are
derived from the query of the parent mak:list
tag, by adding the FROM and WHERE sections. This is done in {@link
org.makumba.view.ComposedQuery}
- listData: the results of
the listQuery
- iterationGroupData: the
part of the listData that is
iterated through during an iterationGroup
- nullableValueQuery: if a mak:value or mak:input (mentioned only as mak:value in what follows) has an
expression of the form label.ptr1.ptr2.ptr3.field and any of ptr1...N is
nullable, that mak:value will
introduce a separate query, whose parentQuery
is the listQuery of the parent mak:list. Consider the following
example
RestaurantCook.mdd person = not null ptr Person favoriteDish = ptr Dish ; NULLABLE!
cookView.jsp <mak:list from="general.RestaurantCook rc" /> <mak:value expr="rc.person.name" /> <mak:value expr="rc.favoriteDish.name" /> </mak:list>
If there would be a single query, then the cooks who have no
favoriteDish pointer filled in (nullable, so it's possible) would not be
returned by the list query (simplified notation):
SELECT p.name, d.name FROM RestaurantCook rc, Person p, Dish d WHERE rc.person = p AND rc.favoriteDish = d ;
because the where condition "rc.favoriteDish = d" cannot be fulfilled
for those cooks that have rc.favoriteDish = NIL. So instead you do the listQuery:
SELECT p.name FROM RestaurantCook rc, Person p WHERE rc.person = p ;
and the nullableValueQuery:
SELECT p.name, d.name FROM RestaurantCook rc, Person p, Dish d WHERE rc.person = p AND rc.favoriteDish = d ;
All mak:value's whose
expression is nullable due to the same pointer should be computed as
projections in the same nullableValueQuery.
- setFieldValueQuery: if a mak:value or mak:input has an expression of the
form label.ptr1...ptrN.setField, that mak:value
will introduce a separate query whose parent is the listQuery of the parent mak:list. setFieldValueQueries are especially
used to generate HTML <select> for mak:inputs
- valueQuery: a nullableValueQuery or setFieldValueQuery
- queryMak:value: mak:value/input that does generates
a valueQuery, i.e. a query that
is forced not by the mak:list per-se, but because of the special data
type of the projections
- nonQueryMak:value: mak:value/input that does not
generate a valueQuery
- valueProjection: a query
projection that corresponds to an nonQueryMak:value.
Basically every nonQueryMak:value needs
to know which query it should read its result from, and which
projection (column) of that query corresponds to that nonQueryMak:value .
The queryMak:values need to
know their valueQuery. In
addition queryMak:values that introduce nullableValueQueries also
need to know which projection of the nullableValueQuery
corresponds to them, because a nullableValueQuerymay
be shared by more mak:values
- listGroup: a rootList and all the
children mak:lists and mak:values. The rootList has no parent, this is
equivalent to having one single parentIteration.
So the {@link org.makumba.view.jsptaglib.QueryTag} of a rootList will never change due to parentIterations. That makes it a
good candidate to store the data of all its enclosed listQueries and valueQueries. Before storing them,
it must execute the queries. So the rootList
of the listGroup will execute
all the listQueries and valueQueries.
- queryProjection: a column
selected in a query (i.e. between SELECT and FROM, comma-separated)
- currentListData: the data
row (all queryProjections) from
the current iteration of a mak:list or mak:object. Also referred to as
the data of an iteration
- currentDataSet: a stack
containing the the currentListData of
each listQuery that is
currently iterating: the data of the current iteration, the data of the parentIteration... and finally the currentDataSet of the rootList at the bottom of the stack. A mak:list will push its current
result in the stack at the begining of each iteration, and will pop it out at
the end.
- keyLabels: the labels
declared in the FROM of a query
<mak:object from="best.johnny.Season ssn" where="ssn.name = $season" >
-> key label is ssn
- keyProjections: queryProjections made from keyLabels.
The keyLabels are always (i
think) selected as projections, whether they are requested in mak:values or not. All key
projections selected in a parent are selected in the children, but not
the reverse.
- The rule of the query rendering engine:
In a currentDataSet, the value
of a keyProjection in a listQuery currentListData is equal to
the value of that keyProjection
in the parentQuery
currentListData, if it is
selected there.
- grouping: to determine an iterationGroupData from a listData (which a {@link
org.makumba.view.jsptaglib.QueryTag} should do when it starts), one has
to choose all rows from the list data that have the keyProjections equal to those
currently iterated by the parentQueriesas
stored in the
currentDataSet. This is
done with the help of class {@link org.makumba.view.Grouper}.
<mak:list from="lbg l"><mak:value "l.name"/> <mak:list from="members m" where="m.homeLbg = l"> <mak:value "m.firstName" /> </mak:list> </mak:list>
will result in
2 queries:
1/ SELECT l, l.name from Lbg l 2/ SELECT l, l.name, m.firstName from Lbg l, Members m WHERE m.homeLbg=l ;
When iterating thru the inner mak:list,
the iterationGroupData is
selected from the entire list data (of query 2) by selecting only that
data for which 'l' (the keyProjection)
is equal to the 'l' of the current parent iteration. In other
words:
iterationGroupData=grouper(listData, currentDataSet)
where listData (for that mak:list) is computed by the root
tag by running that mak:list's listQuery, and stored in the listGroup
actually what happens is
// at group list start Grouper listDataGrouper= new Grouper(dataAboutKeys, listData)
// then, for every parent iteration iterationGroupData=listDataGrouper.getData(currentDataSet)
- to determine a value displayed by queryMak:value, we need to apply the
same pattern
valueQueryData=grouper(valueQueryResult, currentDataSet)
where valueQueryResult (for that mak:value) is computed by the root
tag by running that value query, and stored in the list group for nullableValueQuerymak:values, the valueQueryData can
be of length 0 (a null pointer) or 1 (not-null pointers). For setFieldValueQueries it can be of any
length.
- tagKey: a key that
uniquely identifies a mak:list/object
or mak:value/input/editForm tag
within the page. The key is made from the tag attributes that cannot
cahnge at execution time (mak:list from,
where, orderBy, mak:value expr)
and the key of the parentList.
They key is used for the mak:list/valueto
find its listData at the
begining of a parentIteration
(from which it computes the iterationGroupData),
by mak:value/input/editForm to
find their value in the iterationGroupData
of the parentList (for nonQueryMak:values) or of their own valueQuery
- queryKey: a key that
uniquely identifies a listQuery
or valueQuery within the page.
The querykey of listQueries is the same with their mak:list tagKey. The queryKey of a valueQuery is the queryKey of the parentList plus the path to the
nullable pointer that lead to the creation of the nullableValueQuery (this ensures
that values that are nullable due to the same pointer will share the
same query) or the set field for setValueQuery.
@see org.makumba.view
|