Author: hlship
Date: Fri Oct 20 11:11:11 2006
New Revision: 466222

URL: http://svn.apache.org/viewvc?view=rev&rev=466222
Log:
Patch the component parser to kind of support entities in the template.

Added:
    
tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/html_entity.html
Modified:
    
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/TemplateParserImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/site/resources/tap5devwiki.html
    tapestry/tapestry5/tapestry-core/trunk/src/site/resources/tap5devwiki.xml
    
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/TemplateParserImplTest.java

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/TemplateParserImpl.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/TemplateParserImpl.java?view=diff&rev=466222&r1=466221&r2=466222
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/TemplateParserImpl.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/TemplateParserImpl.java
 Fri Oct 20 11:11:11 2006
@@ -19,6 +19,7 @@
 import static org.apache.tapestry.util.CollectionFactory.newMap;
 import static org.apache.tapestry.util.CollectionFactory.newSet;
 
+import java.io.IOException;
 import java.net.URL;
 import java.util.List;
 import java.util.Map;
@@ -56,8 +57,6 @@
 
 /**
  * Non-threadsafe implementation.
- * 
- * 
  */
 public class TemplateParserImpl extends DefaultHandler implements 
TemplateParser, LexicalHandler
 {
@@ -96,6 +95,8 @@
 
     private boolean _insideBodyErrorLogged;
 
+    private boolean _ignoreEvents;
+
     private final Log _log;
 
     private final Pattern EXPANSION_PATTERN = Pattern.compile(
@@ -109,6 +110,8 @@
         _parserFactory = SAXParserFactory.newInstance();
 
         _parserFactory.setNamespaceAware(true);
+
+        reset();
     }
 
     private void reset()
@@ -123,6 +126,8 @@
         _textIsCData = false;
         _insideBody = false;
         _insideBodyErrorLogged = false;
+        _ignoreEvents = true;
+
     }
 
     public ComponentTemplate parseTemplate(Resource templateResource)
@@ -207,6 +212,9 @@
     @Override
     public void characters(char[] ch, int start, int length) throws 
SAXException
     {
+        if (_ignoreEvents)
+            return;
+
         if (insideBody())
             return;
 
@@ -251,10 +259,10 @@
 
         int startx = 0;
 
-        // The big problem with all this code is that everything gets assigned 
to the 
+        // The big problem with all this code is that everything gets assigned 
to the
         // start of the text block, even if there are line breaks leading up 
to it.
         // That's going to take a lot more work and there are bigger fish to 
fry.
-        
+
         while (matcher.find())
         {
             int matchStart = matcher.start();
@@ -283,6 +291,8 @@
     public void startElement(String uri, String localName, String qName, 
Attributes attributes)
             throws SAXException
     {
+        _ignoreEvents = false;
+
         if (_insideBody)
             throw new IllegalStateException(ServicesMessages
                     .mayNotNestElementsInsideBody(localName));
@@ -437,7 +447,7 @@
 
     public void comment(char[] ch, int start, int length) throws SAXException
     {
-        if (insideBody())
+        if (_ignoreEvents || insideBody())
             return;
 
         processTextBuffer();
@@ -478,7 +488,7 @@
 
     public void startCDATA() throws SAXException
     {
-        if (insideBody())
+        if (_ignoreEvents || insideBody())
             return;
 
         processTextBuffer();
@@ -495,12 +505,14 @@
 
     public void startEntity(String name) throws SAXException
     {
+
     }
 
     @Override
     public void ignorableWhitespace(char[] ch, int start, int length) throws 
SAXException
     {
-
     }
+
+
 
 }

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/site/resources/tap5devwiki.html
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/resources/tap5devwiki.html?view=diff&rev=466222&r1=466221&r2=466222
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/resources/tap5devwiki.html 
(original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/resources/tap5devwiki.html 
Fri Oct 20 11:11:11 2006
@@ -5168,13 +5168,15 @@
        <div id="storeArea"><div tiddler="ComponentActionRequest" 
modifier="HowardLewisShip" modified="200610081335" created="200610080113" 
tags="">Component actions are actions that reflect user interaction with a 
component within a page. Again, this falls into several broad categories:\n\n* 
Links that perform a server-side action, and result in a page refresh, or a new 
page being displayed.\n* Ajax style links, which perform a server-side action, 
and refresh only part of the page.\n* Forms which perform a server-side action, 
followed by a page refresh (or new page being displayed).\n* Ajax style forms, 
which trigger an action, followed by a refresh of part of the page.\n* Other 
user interactions, which result in a server side action, and a partial page 
refresh.\n\nIn all of these cases, one or more ComponentEvents is fired. The 
result of ComponentEvent determines whether a partial page render or a full 
page render occurs.\n\nIn the later case, a client side redirect is sent, to f
 orce the browser to initial a new PageRenderRequest.  This addresses an issue 
in Tapestry 4, in that following a link or form submission, the URL would 
indicate details about the previous page, not the newly displayed page, and 
clicking the browser refresh button could cause a server side operation to 
occur again (which would often be quite undersirable).\n\n!URI 
Format\n\n{{{\n/page-name.event-type/component-id-path/id\n}}}\n\nHere 
page-name is the LogicalPageName.  The event-type is a string that identifies 
the type of event (and will ultimately be used to select an event handler 
method).  \n\nThe component-id-path is a dot-separated series of component ids, 
used to identify a specific component within the overall page.\n\nThe id is 
optional, and may be repeated. The id value or values will be provided to the 
event handler method.\n\nExample: /Login.submit/form  (the URI for a form 
component on page Login).\n\nExample: /admin/UserProfile/action/menu.delete/37  
(component m
 enu.delete of the UserProfile page, with an id of 37).\n</div>
 <div tiddler="ComponentEvent" modifier="HowardLewisShip" 
modified="200610081359" created="200610081351" tags="requests events">Component 
events represent the way in which incoming requests are routed to user-supplied 
Java methods.\n\nComponent events //primarily// originate as a result of a 
ComponentActionRequest, though certain other LifecycleEvents will also 
originate component events.\n\nEach component event contains:\n* An event type; 
a string that identifies the type of event\n* An event source; a component that 
orginates the event (where applicable)\n* A context; an array of strings 
associated with the event\n\nEvent processing starts with the component that 
originates the event.\n\nHandler methods for the event within the component are 
invoked.\n\nIf no handler method aborts the event, then handlers for the 
originating component's container are invoked.\n\nThis containues until 
handlers for the page (the root component) are invoked, or until some handler 
method aborts
  the event.\n\nThe event is aborted when a handler method returns a non-null, 
non-void value.  The interpretation of that value varies based on the type of 
event.\n\nEvents are routed to handler methods using the @~OnEvent 
annotation.\n\nThis annotation is attached to a method within a component 
class.  This method becomes a handler method for an event.\n\nThe annotation 
allows events to be filtered by event type or by originating 
component.\n\n{{{\n  @OnEvent(value=&quot;submit&quot;, 
component=&quot;form&quot;)\n  String handleSubmit()\n  {\n    // . . .\n\n   
return &quot;PostSubmit&quot;;\n  }\n}}}\n\nIn the above hypothetical example, 
a handler method is attached to a particular component's submit event.  After 
processing the data in the form, the LogicalPageName of another page within the 
application is returned. The client browser will be redirected to that 
page.\n\nHandler methods need not be public; they are most often package 
private (which facilitated UnitTesting 
 of the component class).\n\nHandler methods may take parameters.  This is most 
useful with handler methods related to links, rather than forms.\n\nAssociated 
with each event is the context, a set of strings defined by the application 
programmer.\n\nParameters are coerced (see TypeCoercion) from these strings.  
Alternately, a parameter of type String[] receives the set of strings.\n\n{{{\n 
 @OnEvent(component=&quot;delete&quot;)\n  String deleteAccount(long 
accountId)\n  {\n    // . . .\n\n   return &quot;AccountPage&quot;;\n  
}\n}}}\n\nHere, ther first context value has been coerced to a long and passed 
to the deleteAccount() method. Presemuable, an action link on the page, named 
&quot;delete&quot;, is the source of this event.\n\n</div>
 <div tiddler="ComponentMixins" modifier="HowardLewisShip" 
modified="200610051243" created="200610051234" tags="mixins">One of the more 
exciting ideas in Tapestry 5 is //mixins//; the ability to add behavior to a 
component without writing code. \n\nIt is expected that much common behavior, 
especially for form control components, will be provided by mixins. Further, 
many Ajax techniques will take the form of mixins applied to otherwise ordinary 
components.\n\nA mixin is an additional component class that operates //with// 
the main component. For a component element within the page, the functionality 
is provided by the main component class and by\nthe mixin.  \n\nMixins are 
primarily about rendering. Mixin render methods are //mixed in// to the 
components' render methods. In effect, the different rendering phases of a 
component are different AOP-like //joinpoints//, and the mixins can provide 
//before advice//.\n\nMixins can be specified for an //instance// of a 
component, or c
 an be specified as part of the //implementation// of a component.\n\nIn the 
former case, the @Component annotation will be supplemented with a @Mixin 
annotation. The @Mixin is a list of one or more mixin classes for that 
component.\n\n''Todo: Template syntax for mixins?''\n\nIn the latter case, the 
@ComponentClass annotation will be supplemented with a @Mixin 
annotation.\n\nMixins can be configured.  They can have parameters, just like 
ordinary components. When a formal parameter name is ambiguous, it will be 
prefixed with the unqualified class name. Thus, you might have to say, 
&quot;MyMixin.parameterName=someProperty&quot; if &quot;parameterName&quot; is 
ambiguous (by ambiguous, we mean, a parameter of more than one mixin or of the 
component itself).  \n\nThis disambiguation is //simple//. It is assumed that 
the unqualified class name will be sufficient to uniquely identify a mixin. 
That is, it is expected that you will not have the same class name even in 
different packag
 es (as mixins, on a single component). In a //degenerate case// where this is 
not so, it will be necessary to disambiguate the mixin name by create a 
subclass of the mixin with a new name.\n\n''Todo: how are mixins on a component 
implementation configured?''\n\nMixins may have persistent state, just as with 
ordinary components.\n\n</div>
+<div tiddler="ComponentTemplates" modifier="HowardLewisShip" 
modified="200610201807" created="200610201801" tags="">There are some issues 
related to component templates.\n\nFirstly, people are really interested in 
seeing the return of InvisibleInstrumentation.  That is coming.\n\nSecondly, 
the idea that templates are well-formed XML documents is causing some 
issues.\n\nThe problem is related to entities and doctypes.\n\nUnless you 
provide a doctype for the template, 
[[entities|http://www.htmlhelp.com/reference/html40/entities/]] don't work; 
they result in template parse errors.\n\nIf you provide a standard doctype, 
say\n{{{\n &lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0 
Transitional//EN&quot;\n            
&quot;http://www.w3.org/TR/REC-html40/loose.dtd&quot;&gt;\n}}}\n\nYou also get 
parse errors, because the DTD does some odd things with comments that the Java 
SAX parser doesn't seem to understand.\n\nI've had better luck with the XHTML 
doctype:\n{{{\n&lt;!DOCTYPE htm
 l PUBLIC &quot;-//W3C//DTD XHTML 1.0 
Transitional//EN&quot;\n&quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;\n}}}\n\nBut
 this doesn't render quite the way I want it to.\n\nFurther, entities in the 
text are converted to unicode by the parser, then converted to &lt;numeric&gt; 
entities on output.  Not quite WYSIWYG and potentially confusing.\n\nIt may be 
necessary to discard SAX and build a limited XML parser that allows entities to 
be passed through unchanged (they would become a special type of document 
token).\n\nLastly, the question is how to get the correct DOCTYPE into the 
rendered output, espcially in the common case that a Border component provides 
the outer tags, as is common in Tapestry 4.  This may have to be configured as 
a annotation on page classes.</div>
 <div tiddler="DynamicPageState" modifier="HowardLewisShip" 
modified="200609211635" created="200609211610" tags="">Tapestry 4 has left 
tracking of dynamic page state as an exercise to the developer.  Mostly, this 
is done using the ''parameters'' parameter of the ~DirectLink 
component.\n\nDynamic page state is anything that isn't inside a persistent 
page property. For the most part, this includes page properties updated by a 
For component\n\nIt seems likely that this information could be automatically 
encoded into ~URLs.  \n\nI'm envisioning a service that accumulates a series of 
//commands//. Each command is used to store a bit of page state. The commands 
are serializable.  The commands are ultimately serialized into a MIME string 
and attached as a query parameter to each URL.\n\nWhen such a link is 
triggered, the commands are de-serialized and each executed in turn. Only when 
that is finished is any further event processing executed, including calling 
into to user code.\n\nM
 y outline for this is to store a series of tuples; each tuple is a component 
id plus the command to execute.\n\n{{{\npublic interface 
ComponentCommand&lt;T&gt;\n{\n  void execute(T component);\n}\n}}}\n\nThese 
commands should be immutable.\n\nSo a component, such as a For loop component, 
could provide itself and a ComponentCommand instance (probably a static inner 
class) to some kind of PageStateTracker service.\n\n{{{\npublic interface 
PageStateTracker\n{\n  void &lt;T&gt; addCommand(T component, 
ComponentCommand&lt;T&gt; command);\n}\n}}}\n\nThe commands are kept in the 
order that they are added, except that new commands for the same component 
//replace// previous commands for that component.\n\nAs with the Tapestry 4 For 
component, some mechanism will be needed to store object ids inside the URLs 
(that is, inside the commands serialized into URL query parameters) and 
translate back to //equivalent// objects when the link is triggered.\n\nDynamic 
page state outside of a Fo
 rm will overlap with some of the FormProcessing inside the form.</div>
 <div tiddler="EditTemplate" modifier="HowardLewisShip" modified="200609210649" 
created="200609210648" tags="">&lt;div class='toolbar' macro='toolbar 
+saveTiddler -cancelTiddler deleteTiddler'&gt;&lt;/div&gt;\n&lt;div 
class='title' macro='view title'&gt;&lt;/div&gt;\n&lt;div class='editor' 
macro='edit title'&gt;&lt;/div&gt;\n&lt;div class='editor' macro='edit 
text'&gt;&lt;/div&gt;\n&lt;div class='editor' macro='edit 
tags'&gt;&lt;/div&gt;&lt;div class='editorFooter'&gt;&lt;span macro='message 
views.editor.tagPrompt'&gt;&lt;/span&gt;&lt;span 
macro='tagChooser'&gt;&lt;/span&gt;&lt;/div&gt;</div>
 <div tiddler="EnvironmentalServices" modifier="HowardLewisShip" 
modified="200609260145" created="200609251547" tags="">Frequently, different 
components need to //cooperate// during the rendering process.\n\nThis is an 
established pattern from Tapestry 4, which an enclosing component provides 
services to the components it encloses. By //encloses// we mean, any components 
that are rendered as part of the Form's body; give the use of the 
Block/~RenderBlock components, this can not be determined statically, but is 
instead determined dynamically, as part of the rendering process.\n\nThe 
canoncial example of this pattern is Form component, and the complex 
relationship it has with each form element component it encloses.\n\nIn 
Tapestry 4, this mechanism was based on the ~IRequestCycle which could store 
named attributes. The service providing component would store itself into the 
cycle using a well known name, and service consuming components would retrieve 
the service using the sam
 e well known name.\n\nFor Tapestry 5, this will be formalized. A new service 
will be used to manage this information:\n\n{{{\npublic interface 
Enviroment\n{\n  &lt;T&gt; T push(Class&lt;T&gt; type, T instance);\n\n  
&lt;T&gt; peek(Class&lt;T&gt; type);\n\n  &lt;T&gt; T pop(Class&lt;T&gt; 
type);\n}\n}}}\n\nThe Environment is unique to a request.</div>
 <div tiddler="FormProcessing" modifier="HowardLewisShip" 
modified="200609211540" created="200609210203" tags="forms">Form processing in 
Tapestry 4 had certain strengths and limitations.\n\nBasically, any action 
framework that can do a simple mapping from query parameters to bean property 
names has advantages in terms of simple forms, and Tapestry 4's approach has 
huge advantages on more complex forms (with some considerable developer and 
framework overhead).\n\nWith a direct mapping of query parameter names to bean 
names, each query parameter becomes self describing. You map query parameters 
to property of some well known bean. You do simple conversions from strings to 
other types (typically, ints and dates and the like). You drop query parameters 
that don't match up. You leave a lot of validation and other plumbing (such as 
getting those values into your DataTransferObjects) to the developer.\n\nBut 
you never see a ~StaleLinkException.\n\nYou also have some unwanted loophol
 es in your application in that //any// property can be updated through the 
URL. This is //one step// towards a security hole.\n\n!Tapestry 4 
Approach\n\nEvery form component, as it renders, asks the Form that encloses it 
to provide a client id.  The terminology is a little messed; client id is the 
unique (within the form) name for //one rendering// of the component. If the 
component renders multiple times, because of loops, each rendering gets a 
unique name.  This becomes the &lt;input&gt;'s name attribute, and ultimately, 
the query parameter name.\n\nTapestry attempts to make the client id match the 
(user provided) component id. This is not always possible, especially in a 
loop, in which case a numeric suffix may be appended to the id to (help) ensure 
uniqueness.\n\nOn render, a sequence of //component activations// occur, guided 
by the normal render sequence. The exact sequence of activations guides\nthe 
production of client ids.\n\nUsing more advanced Tapestry techniques,
  including loops, conditionals and the Block/RenderBlock combo, the exact set 
of components and\ncomponent activations that will occur for a given rendering 
of a given form can not be predicted statically. Tapestry must actually render 
out the form\nto discover all of these.\n\nIn fact, while the Form component is 
producing this series of client ids, it builds up the list and stores it into 
the rendered page as a hidden form field. It will need it later, when the 
client-side form is submitted back to the server.\n\nAn advantage of this 
approach is the disconnect between the query parameter names (the client ids) 
and the objects and properties being editted. Often the client ids will be 
//mneumonic// for the properties, but aren't directly mapped to them. Only the 
components responsible for each query parameter know how to validate the 
submitted value, and what property of which object will need to be 
updated.\n\nWhen a form submission occurs, we want to ensure that each quer
 y parameter value read out of the request is applied to the correct property 
of the correct object. There's a limit to how much Tapestry can help here 
(because it has only a casual knowledge of this aspect of the application 
structure).\n\nDuring this submission process, which endded up with the curious 
name, //rewind phase//, Tapestry must do two things:\n* Activate each 
component, such that the component may re-determine its client id, read its 
parameter, and update its page property\n* Validate that the process has not 
been comprimised by a change of server side state\n\nThat second element is a 
tricky one; things can go wonky if a race condition occurs between two users. 
For example, lets take a simple invoice and line item model. If users A and B 
both read the same invoice, user A adds a line item, and user B changes a line 
item ... we can have a problem when user B submits the form. Now that there are 
three line items (not two) in the form, there will be extra componen
 t activations to process query parameters that don't exist in the request. 
\n\nThis scenario can occur whenever the processing of the form submission is 
driven by server-side data that can change between request.\n\nTapestry detects 
this as a difference in the sequence of client ids allocated, and throws a 
~StaleLinkException, which is very frustrating for developers to comprehend and 
fix.\n\nThere are also other edge cases for different race conditions where 
data is applied to the wrong server-side objects.\n\nThe Tapestry 3 ~ListEdit 
component, which evolved into the  Tapestry 4 For component, attempts to 
address this by serializing a series of //object ids// into the form (as a 
series of hidden fields). This requires a bit of work on the part of the 
developer to provide an ~IPrimaryKeyConverter that can help convert objects to 
ids (when rendering) and ids back to objects (during form 
submission).\n\nGenerally speaking, the Tapestry 4 approach represents layers 
of kludge o
 n layers of kludge. It works, it gets the job done, it can handle some very 
complex situations, but it is less than ideal.\n\n!Tapestry 5\n\nThe goal here 
is to capture the series of //component activations//, along with any 
significant page state changes, during the render.\n\nThese activations will be 
a series of //commands//.  For each component activation there will be two 
commands:  the first command will be used to inform the component of its client 
id (this command executes during render and during form submission). The second 
command will request that the client handle the form submission (this command 
executes only during form submission).\n\nThe serialized series of commands is 
stored as a hidden form field.\n\nThere's a lot of API to be figured out, 
especially the relationship between the form components and the form 
itself.\n\nFurther, a lot of what the Tapestry 4 For component does, in terms 
of serializing dynamic page state, will need to fold into this as well.
 \n\nThe end result will be a single hidden field with a big MIME string inside 
it ... but compared to the Tapestry 4 Form component (which has to write out 
many hidden fields) the whole will be less than the sum of the parts ... due to 
the overhead of serialization and gzip compression.\n\n\n\n\n\n\n</div>
+<div tiddler="InvisibleInstrumentation" modifier="HowardLewisShip" 
modified="200610201803" created="200610201802" tags="">A feature of Tapestry 4 
where the component id, type and parameters were &quot;hidden&quot; inside 
ordinary HTML tags.\n\nThis will show up inside Tapestry 5 pretty soon, and 
look something like:\n{{{\n&lt;span t:type=&quot;If&quot; 
t:test=&quot;prop:showWarning&quot; class=&quot;warning&quot;&gt; \n  . . 
.\n&lt;/span&gt;\n}}}</div>
 <div tiddler="LogicalPageName" modifier="HowardLewisShip" 
modified="200610081330" created="200610081330" tags="">A logical page name is 
the name of a page as it is represented in a URI.\n\nInternally, Tapestry 
operates on pages using full qualified class names. Technically, the FQCN is 
the class of the page's root element, but from an end developer point of view, 
the root element is the page.\n\nThe logical page name must be converted to a 
fully qualified class name.\n\nA set of LibraryMappings are used.  Each library 
mapping is used to express a folder name, such as &quot;core&quot;, with a Java 
package name, such as org.apache.tapestry.corelib.  For pages, the page name is 
searched for in the pages sub-package (i.e., 
org.apache.tapestry.corelib.pages).  Component libraries have unique folder 
names mapped to root packages that contain the pages (and components, and 
mixins) of that library.\n\nWhen there is no folder name, the page is expected 
to be part of the application, 
 under the pages sub-package of the application's root package.\n\nIf not found 
there, as a special case, the name is treated as if it were prefixed with 
&quot;core/&quot;.  This allows access to the core pages (and more importantly, 
components -- the search algorithm is the same).\n\nFinally, pages may be 
organized into folders.  These folders become further sub-packages. Thus as 
page name of &quot;admin/EditUsers&quot; may be resolved to class 
org.example.myapp.pages.admin.EditUsers.\n\n</div>
 <div tiddler="MainMenu" modifier="HowardLewisShip" modified="200609210701" 
created="200609210643" tags="">MasterIndex\n[[RSS 
feed|tap5devwiki.xml]]\n\n[[Tapestry 5 
Home|http://tapestry.apache.org/tapestry5/]]\n[[Howard's 
Blog|http://howardlewisship.com/blog/]]\n\n[[Formatting 
Help|http://www.blogjones.com/TiddlyWikiTutorial.html#EasyToEdit%20Welcome%20NewFeatures%20WhereToFindHelp]]</div>
-<div tiddler="MasterIndex" modifier="HowardLewisShip" modified="200610071244" 
created="200609202214" tags="">Top level concepts within Tapestry 5.\n\n* 
PropBinding -- Notes on the workhorse &quot;prop:&quot; binding prefix\n* 
TypeCoercion -- How Tapestry 5 extensibly addresses type conversion\n* 
FormProcessing\n* DynamicPageState -- tracking changes to page state during the 
render\n* EnvironmentalServices -- how components cooperate during page 
render\n* ComponentMixins -- A new fundamental way to build web 
functionality\n* RequestTypes -- Requests, request processing, URL formats</div>
+<div tiddler="MasterIndex" modifier="HowardLewisShip" modified="200610201757" 
created="200609202214" tags="">Top level concepts within Tapestry 5.\n\n* 
PropBinding -- Notes on the workhorse &quot;prop:&quot; binding prefix\n* 
TypeCoercion -- How Tapestry 5 extensibly addresses type conversion\n* 
FormProcessing\n* DynamicPageState -- tracking changes to page state during the 
render\n* EnvironmentalServices -- how components cooperate during page 
render\n* ComponentMixins -- A new fundamental way to build web 
functionality\n* RequestTypes -- Requests, request processing, URL formats\n* 
ComponentTemplates -- Issues about Component Templates</div>
 <div tiddler="OGNL" modifier="HowardLewisShip" modified="200610071249" 
created="200609202254" tags="">The [[Object Graph Navigation 
Library|http://ognl.org]] was an essential part of Tapestry 4.\n\nOGNL is both 
exceptionally powerful (especially the higher order things it can do, such as 
list selections and projections). However, for the highest\nend sites, it is 
also a performance problem, both because of its heavy use of reflection, and 
because it uses a lot of code inside synchronized blocks.\n\nIt will be 
optional in Tapestry 5. I believe it will not be part of the tapestry-core, but 
may be packaged as tapestry-ognl.\n\nThe &quot;prop:&quot; binding prefix is an 
effective replacement for OGNL in Tapestry 5.   See PropBinding.\n</div>
 <div tiddler="PageRenderRequest" modifier="HowardLewisShip" 
modified="200610081333" created="200610071313" tags="">Page render requests are 
requests used to render a specific page.  //render// is the term meaning to 
compose the HTML response to be sent to the client. Note: HTML is used here 
only as the most common case, other markups are entirely possible.\n\nIn many 
cases, pages are stand-alone.  No extra information in the URL is necesarry to 
render them.  PersistentProperties of the page will factor in to the rendering 
of the page.\n\nIn specific cases, a page needs to render within a particular 
context. The most common example of this is a page that is used to present a 
specific instance of a database persistent entity. In such a case, the page 
must be combined with additional data, in the URL, to identify the specific 
entity to access and render.\n\n! URI 
Format\n\n{{{\n/page-name.html/id\n}}}\n\nHere &quot;page-name&quot; is the 
LogicalPageName for the page. \n\nThe &q
 uot;.html&quot; file extension is used as a delimiter between the page name 
portion of the URI, and the context portion of the URI. This is necessary 
because it is not possible (given the plethora of libraries and folders) to 
determine how many slashes will appear in the URI.\n\nThe context consists of 
one ore more ids (though a single id is the normal case). The id is used to 
identify the specific data to be displayed. Further, a page may require 
multiple ids, which will separated with slashes. Example: 
/admin/DisplayDetail.html/loginfailures/2006\n\nNote that these context values, 
the ids, are simply //strings//. Tapestry 4 had a mechanism, the DataSqueezer, 
that would encode the type of object with its value, as a single string, and 
convert it back. While seemingly desirable, this facility was easy to abuse, 
resulting in long and extremely ugly URIs.\n\nAny further information needed by 
Tapestry will be added to the URI as query parameters. This may include things 
like us
 er locale, persistent page properties, applicaition flow identifiers, or 
anything else we come up with.\n\n! Request Processing\n\nOnce the page and id 
parameters are identified, the corresponding page will be loaded.\n\nTapestry 
will fire two events before rendering the page.\n\nThe first event is of type 
&quot;setupPageRender&quot;.  This allows the page to process the context (the 
set of ids). This typically involves reading objects from an external 
persistent store (a database)\nand storing those objects into transient page 
properties, in expectaion of the render.\n\nThe @SetupPageRender annotation 
marks a method to be invoked when this event is triggered.  The method may take 
one or more strings, or an array of strings, as parameters; these will be\nthe 
context values.  The method will normally return void.  Other values are 
''TBD''. It may also take other simple types, which will be coerced from the 
string [EMAIL PROTECTED] setup(long id)\n{\n  . .
  .\n}\n}}}\n\n\n\nThe second event is of type &quot;pageValidate&quot;.  It 
allows the page to decide whether the page is valid for rendering at this time. 
This most often involves a check to see if the user is logged into the 
application, and has the necessary privileges to display the contents of the 
page.  User identity and privileges are //not// concepts built into Tapestry, 
but are fundamental to the majority of Tapestry applications.</div>
 <div tiddler="PropBinding" modifier="HowardLewisShip" modified="200610201450" 
created="200609202203" tags="bindings">The &quot;prop:&quot; binding prefix is 
the default in a  lot of cases, i.e., in any Java code (annotations).\n\nThis 
binding prefix  supports several common idioms even though they are not, 
precisely, the names of properties.  In many cases, this will save developers 
the bother of using a &quot;literal:&quot; prefix.\n\nThe goal of the 
&quot;prop:&quot; prefix is to be highly efficient and useful in 90%+ of the 
cases. [[OGNL]], or synthetic properties in the component class, will pick up 
the remaining cases.\n\n!Numeric literals\n\nSimple numeric literals should be 
parsed into read-only, invariant 
bindings.\n{{{\nprop:5\n\nprop:-22.7\n}}}\n\nThe resulting objects will be of 
type Long or type Double. TypeCoercion will ensure that component parameters 
get values (say, int or float) of the correct type.\n\n!Range 
literals\n\nExpresses a range of integer values, 
 either ascending or descending.\n{{{\nprop:1..10\n\nprop:100..-100\n}}}\n\nThe 
value of such a binding is Iterable; it can be used by the Loop 
component.\n\n!Boolean literals\n\n&quot;true&quot; and &quot;false&quot; 
should also be converted to invariant 
bindings.\n{{{\nprop:true\n\nprop:false\n}}}\n\n!String literals\n\n//Simple// 
string literals, enclosed in single quotes.  Example:\n{{{\nprop:'Hello 
World'\n}}}\n\n//Remember that the binding expression will always be enclosed 
in double quotes.//\n\n!This literal\n\nIn some cases, it is useful to be able 
to identify the current component:\n{{{\nprop:this\n}}}\n\nEven though a 
component is not immutable, the value of //this// does not ever change,\nand 
this binding is also invariant.\n\n!Null literal\n\n{{{\nprop:null\n}}}\n\nThis 
value is always exactly null. This can be used to set a parameter who'se 
default value is non-null to the explicit value null.\n\n!Property 
paths\n\nMulti-step property paths are extremely importa
 nt.\n\n{{{\nprop:poll.title\n\nprop:identity.user.name\n}}}\n\nThe initial 
terms need to be readable, they are never updated. Only the final property name 
must be read/write, and in fact, it is valid to be read-only or 
write-only.\n\nThe prop: binding factory builds a Java expression to read and 
update properties. It does not use reflection at runtime. Therefore, the 
properties of the //declared// type are used. By contrast, [[OGNL]] uses the 
//actual// type, which is reflection-intensive. Also, unlike OGNL, errors (such 
as missing properties in the property path) are identified when the page is 
loaded, rather than when the expression is evaluated.\n</div>

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/site/resources/tap5devwiki.xml
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/resources/tap5devwiki.xml?view=diff&rev=466222&r1=466221&r2=466222
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/resources/tap5devwiki.xml 
(original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/resources/tap5devwiki.xml 
Fri Oct 20 11:11:11 2006
@@ -6,16 +6,34 @@
 <description>The quick and dirty one-stop shopping of random ideas for 
Tapestry 5.</description>
 <language>en-us</language>
 <copyright>Copyright 2006 HowardLewisShip</copyright>
-<pubDate>Fri, 20 Oct 2006 14:50:21 GMT</pubDate>
-<lastBuildDate>Fri, 20 Oct 2006 14:50:21 GMT</lastBuildDate>
+<pubDate>Fri, 20 Oct 2006 18:07:34 GMT</pubDate>
+<lastBuildDate>Fri, 20 Oct 2006 18:07:34 GMT</lastBuildDate>
 <docs>http://blogs.law.harvard.edu/tech/rss</docs>
 <generator>TiddlyWiki 2.0.11</generator>
 <item>
+<title>ComponentTemplates</title>
+<description>There are some issues related to component templates.&lt;br 
/&gt;&lt;br /&gt;Firstly, people are really interested in seeing the return of 
InvisibleInstrumentation.  That is coming.&lt;br /&gt;&lt;br /&gt;Secondly, the 
idea that templates are well-formed XML documents is causing some issues.&lt;br 
/&gt;&lt;br /&gt;The problem is related to entities and doctypes.&lt;br 
/&gt;&lt;br /&gt;Unless you provide a doctype for the template, 
[[entities|http://www.htmlhelp.com/reference/html40/entities/]] don't work; 
they result in template parse errors.&lt;br /&gt;&lt;br /&gt;If you provide a 
standard doctype, say&lt;br /&gt;{{{&lt;br /&gt; &lt;!DOCTYPE HTML PUBLIC 
&quot;-//W3C//DTD HTML 4.0 Transitional//EN&quot;&lt;br /&gt;            
&quot;http://www.w3.org/TR/REC-html40/loose.dtd&quot;&gt;&lt;br /&gt;}}}&lt;br 
/&gt;&lt;br /&gt;You also get parse errors, because the DTD does some odd 
things with comments that the Java SAX parser doesn't seem to understand.&lt;br 
/&gt;&l
 t;br /&gt;I've had better luck with the XHTML doctype:&lt;br /&gt;{{{&lt;br 
/&gt;&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 
Transitional//EN&quot;&lt;br 
/&gt;&quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;&lt;br
 /&gt;}}}&lt;br /&gt;&lt;br /&gt;But this doesn't render quite the way I want 
it to.&lt;br /&gt;&lt;br /&gt;Further, entities in the text are converted to 
unicode by the parser, then converted to &lt;numeric&gt; entities on output.  
Not quite WYSIWYG and potentially confusing.&lt;br /&gt;&lt;br /&gt;It may be 
necessary to discard SAX and build a limited XML parser that allows entities to 
be passed through unchanged (they would become a special type of document 
token).&lt;br /&gt;&lt;br /&gt;Lastly, the question is how to get the correct 
DOCTYPE into the rendered output, espcially in the common case that a Border 
component provides the outer tags, as is common in Tapestry 4.  This may have 
to be configured as a annotation on page class
 es.</description>
+<link>http://tapestry.apache.org/tapestry5/tap5devwiki.html#ComponentTemplates</link>
+<pubDate>Fri, 20 Oct 2006 18:07:34 GMT</pubDate>
+</item>
+<item>
+<title>InvisibleInstrumentation</title>
+<description>A feature of Tapestry 4 where the component id, type and 
parameters were &quot;hidden&quot; inside ordinary HTML tags.&lt;br /&gt;&lt;br 
/&gt;This will show up inside Tapestry 5 pretty soon, and look something 
like:&lt;br /&gt;{{{&lt;br /&gt;&lt;span t:type=&quot;If&quot; 
t:test=&quot;prop:showWarning&quot; class=&quot;warning&quot;&gt; &lt;br /&gt;  
. . .&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;}}}</description>
+<link>http://tapestry.apache.org/tapestry5/tap5devwiki.html#InvisibleInstrumentation</link>
+<pubDate>Fri, 20 Oct 2006 18:03:21 GMT</pubDate>
+</item>
+<item>
+<title>MasterIndex</title>
+<description>Top level concepts within Tapestry 5.&lt;br /&gt;&lt;br /&gt;* 
PropBinding -- Notes on the workhorse &quot;prop:&quot; binding prefix&lt;br 
/&gt;* TypeCoercion -- How Tapestry 5 extensibly addresses type 
conversion&lt;br /&gt;* FormProcessing&lt;br /&gt;* DynamicPageState -- 
tracking changes to page state during the render&lt;br /&gt;* 
EnvironmentalServices -- how components cooperate during page render&lt;br 
/&gt;* ComponentMixins -- A new fundamental way to build web 
functionality&lt;br /&gt;* RequestTypes -- Requests, request processing, URL 
formats&lt;br /&gt;* ComponentTemplates -- Issues about Component 
Templates</description>
+<link>http://tapestry.apache.org/tapestry5/tap5devwiki.html#MasterIndex</link>
+<pubDate>Fri, 20 Oct 2006 17:57:13 GMT</pubDate>
+</item>
+<item>
 <title>PropBinding</title>
 <description>The &quot;prop:&quot; binding prefix is the default in a  lot of 
cases, i.e., in any Java code (annotations).&lt;br /&gt;&lt;br /&gt;This 
binding prefix  supports several common idioms even though they are not, 
precisely, the names of properties.  In many cases, this will save developers 
the bother of using a &quot;literal:&quot; prefix.&lt;br /&gt;&lt;br /&gt;The 
goal of the &quot;prop:&quot; prefix is to be highly efficient and useful in 
90%+ of the cases. [[OGNL]], or synthetic properties in the component class, 
will pick up the remaining cases.&lt;br /&gt;&lt;br /&gt;!Numeric 
literals&lt;br /&gt;&lt;br /&gt;Simple numeric literals should be parsed into 
read-only, invariant bindings.&lt;br /&gt;{{{&lt;br /&gt;prop:5&lt;br 
/&gt;&lt;br /&gt;prop:-22.7&lt;br /&gt;}}}&lt;br /&gt;&lt;br /&gt;The resulting 
objects will be of type Long or type Double. TypeCoercion will ensure that 
component parameters get values (say, int or float) of the correct type.&lt;br 
/&gt;&l
 t;br /&gt;!Range literals&lt;br /&gt;&lt;br /&gt;Expresses a range of integer 
values, either ascending or descending.&lt;br /&gt;{{{&lt;br 
/&gt;prop:1..10&lt;br /&gt;&lt;br /&gt;prop:100..-100&lt;br /&gt;}}}&lt;br 
/&gt;&lt;br /&gt;The value of such a binding is Iterable; it can be used by the 
Loop component.&lt;br /&gt;&lt;br /&gt;!Boolean literals&lt;br /&gt;&lt;br 
/&gt;&quot;true&quot; and &quot;false&quot; should also be converted to 
invariant bindings.&lt;br /&gt;{{{&lt;br /&gt;prop:true&lt;br /&gt;&lt;br 
/&gt;prop:false&lt;br /&gt;}}}&lt;br /&gt;&lt;br /&gt;!String literals&lt;br 
/&gt;&lt;br /&gt;//Simple// string literals, enclosed in single quotes.  
Example:&lt;br /&gt;{{{&lt;br /&gt;prop:'Hello World'&lt;br /&gt;}}}&lt;br 
/&gt;&lt;br /&gt;//Remember that the binding expression will always be enclosed 
in double quotes.//&lt;br /&gt;&lt;br /&gt;!This literal&lt;br /&gt;&lt;br 
/&gt;In some cases, it is useful to be able to identify the current 
component:&lt;br /&gt;{{{&
 lt;br /&gt;prop:this&lt;br /&gt;}}}&lt;br /&gt;&lt;br /&gt;Even though a 
component is not immutable, the value of //this// does not ever change,&lt;br 
/&gt;and this binding is also invariant.&lt;br /&gt;&lt;br /&gt;!Null 
literal&lt;br /&gt;&lt;br /&gt;{{{&lt;br /&gt;prop:null&lt;br /&gt;}}}&lt;br 
/&gt;&lt;br /&gt;This value is always exactly null. This can be used to set a 
parameter who'se default value is non-null to the explicit value null.&lt;br 
/&gt;&lt;br /&gt;!Property paths&lt;br /&gt;&lt;br /&gt;Multi-step property 
paths are extremely important.&lt;br /&gt;&lt;br /&gt;{{{&lt;br 
/&gt;prop:poll.title&lt;br /&gt;&lt;br /&gt;prop:identity.user.name&lt;br 
/&gt;}}}&lt;br /&gt;&lt;br /&gt;The initial terms need to be readable, they are 
never updated. Only the final property name must be read/write, and in fact, it 
is valid to be read-only or write-only.&lt;br /&gt;&lt;br /&gt;The prop: 
binding factory builds a Java expression to read and update properties. It does 
not use r
 eflection at runtime. Therefore, the properties of the //declared// type are 
used. By contrast, [[OGNL]] uses the //actual// type, which is 
reflection-intensive. Also, unlike OGNL, errors (such as missing properties in 
the property path) are identified when the page is loaded, rather than when the 
expression is evaluated.&lt;br /&gt;</description>
 <category>bindings</category>
 <link>http://tapestry.apache.org/tapestry5/tap5devwiki.html#PropBinding</link>
-<pubDate>Fri, 20 Oct 2006 14:50:21 GMT</pubDate>
+<pubDate>Fri, 20 Oct 2006 14:50:00 GMT</pubDate>
 </item>
 <item>
 <title>ComponentEvent</title>
@@ -57,12 +75,6 @@
 <pubDate>Sat, 07 Oct 2006 12:49:00 GMT</pubDate>
 </item>
 <item>
-<title>MasterIndex</title>
-<description>Top level concepts within Tapestry 5.&lt;br /&gt;&lt;br /&gt;* 
PropBinding -- Notes on the workhorse &quot;prop:&quot; binding prefix&lt;br 
/&gt;* TypeCoercion -- How Tapestry 5 extensibly addresses type 
conversion&lt;br /&gt;* FormProcessing&lt;br /&gt;* DynamicPageState -- 
tracking changes to page state during the render&lt;br /&gt;* 
EnvironmentalServices -- how components cooperate during page render&lt;br 
/&gt;* ComponentMixins -- A new fundamental way to build web 
functionality&lt;br /&gt;* RequestTypes -- Requests, request processing, URL 
formats</description>
-<link>http://tapestry.apache.org/tapestry5/tap5devwiki.html#MasterIndex</link>
-<pubDate>Sat, 07 Oct 2006 12:44:00 GMT</pubDate>
-</item>
-<item>
 <title>ComponentMixins</title>
 <description>One of the more exciting ideas in Tapestry 5 is //mixins//; the 
ability to add behavior to a component without writing code. &lt;br /&gt;&lt;br 
/&gt;It is expected that much common behavior, especially for form control 
components, will be provided by mixins. Further, many Ajax techniques will take 
the form of mixins applied to otherwise ordinary components.&lt;br /&gt;&lt;br 
/&gt;A mixin is an additional component class that operates //with// the main 
component. For a component element within the page, the functionality is 
provided by the main component class and by&lt;br /&gt;the mixin.  &lt;br 
/&gt;&lt;br /&gt;Mixins are primarily about rendering. Mixin render methods are 
//mixed in// to the components' render methods. In effect, the different 
rendering phases of a component are different AOP-like //joinpoints//, and the 
mixins can provide //before advice//.&lt;br /&gt;&lt;br /&gt;Mixins can be 
specified for an //instance// of a component, or can be specified 
 as part of the //implementation// of a component.&lt;br /&gt;&lt;br /&gt;In 
the former case, the @Component annotation will be supplemented with a @Mixin 
annotation. The @Mixin is a list of one or more mixin classes for that 
component.&lt;br /&gt;&lt;br /&gt;''Todo: Template syntax for mixins?''&lt;br 
/&gt;&lt;br /&gt;In the latter case, the @ComponentClass annotation will be 
supplemented with a @Mixin annotation.&lt;br /&gt;&lt;br /&gt;Mixins can be 
configured.  They can have parameters, just like ordinary components. When a 
formal parameter name is ambiguous, it will be prefixed with the unqualified 
class name. Thus, you might have to say, 
&quot;MyMixin.parameterName=someProperty&quot; if &quot;parameterName&quot; is 
ambiguous (by ambiguous, we mean, a parameter of more than one mixin or of the 
component itself).  &lt;br /&gt;&lt;br /&gt;This disambiguation is //simple//. 
It is assumed that the unqualified class name will be sufficient to uniquely 
identify a mixin. That is
 , it is expected that you will not have the same class name even in different 
packages (as mixins, on a single component). In a //degenerate case// where 
this is not so, it will be necessary to disambiguate the mixin name by create a 
subclass of the mixin with a new name.&lt;br /&gt;&lt;br /&gt;''Todo: how are 
mixins on a component implementation configured?''&lt;br /&gt;&lt;br 
/&gt;Mixins may have persistent state, just as with ordinary components.&lt;br 
/&gt;&lt;br /&gt;</description>
 <category>mixins</category>
@@ -125,18 +137,6 @@
 <description>&lt;div class='toolbar' macro='toolbar +saveTiddler 
-cancelTiddler deleteTiddler'&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class='title' 
macro='view title'&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class='editor' 
macro='edit title'&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class='editor' 
macro='edit text'&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class='editor' macro='edit 
tags'&gt;&lt;/div&gt;&lt;div class='editorFooter'&gt;&lt;span macro='message 
views.editor.tagPrompt'&gt;&lt;/span&gt;&lt;span 
macro='tagChooser'&gt;&lt;/span&gt;&lt;/div&gt;</description>
 <link>http://tapestry.apache.org/tapestry5/tap5devwiki.html#EditTemplate</link>
 <pubDate>Thu, 21 Sep 2006 06:49:00 GMT</pubDate>
-</item>
-<item>
-<title>SiteTitle</title>
-<description>Tapestry 5 Brain Dump</description>
-<link>http://tapestry.apache.org/tapestry5/tap5devwiki.html#SiteTitle</link>
-<pubDate>Wed, 20 Sep 2006 22:49:00 GMT</pubDate>
-</item>
-<item>
-<title>SiteSubtitle</title>
-<description>&lt;br /&gt;The quick and dirty one-stop shopping of random ideas 
for Tapestry 5.</description>
-<link>http://tapestry.apache.org/tapestry5/tap5devwiki.html#SiteSubtitle</link>
-<pubDate>Wed, 20 Sep 2006 22:49:00 GMT</pubDate>
 </item>
 </channel>
 </rss>

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/TemplateParserImplTest.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/TemplateParserImplTest.java?view=diff&rev=466222&r1=466221&r2=466222
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/TemplateParserImplTest.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/TemplateParserImplTest.java
 Fri Oct 20 11:11:11 2006
@@ -171,6 +171,25 @@
 
         assertEquals(t.getText().trim(), "lt:< gt:> amp:&");
     }
+    
+    @Test(enabled=true)
+    void html_entity() throws Exception
+    {
+        List<TemplateToken> tokens = tokens("html_entity.html");
+
+        assertEquals(tokens.size(), 3);
+
+        TextToken t = get(tokens, 1);
+
+        // HTML entities are parsed into values that will ultimately
+        // be output as numeric entities. This is less than ideal; would like
+        // to find a way to keep the entities in their original form (possibly
+        // involving a new type of token), but SAX seems to be fighting me on 
this.
+        // You have to have a DOCTYPE just to parse a template that uses
+        // an HTML entity.
+
+        assertEquals(t.getText().trim(), "nbsp:[\u00a0]");    
+    }
 
     @Test
     void cdata() throws Exception

Added: 
tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/html_entity.html
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/html_entity.html?view=auto&rev=466222
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/html_entity.html
 (added)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/html_entity.html
 Fri Oct 20 11:11:11 2006
@@ -0,0 +1,5 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd";>
+<html>
+    nbsp:[&nbsp;]
+</html>
\ No newline at end of file


Reply via email to