Changeset 104
- Timestamp:
- 06/04/05 00:09:56 (4 years ago)
- Files:
-
- trunk/doc/dsp-reference/tags.html (modified) (4 diffs)
- trunk/dsp/DSPGrammar.d (modified) (6 diffs)
- trunk/dsp/DSPProcessor.d (modified) (2 diffs)
- trunk/dsp/ServletCompilationContext.d (modified) (1 diff)
- trunk/dsp/ServletGenerator.d (modified) (6 diffs)
- trunk/dsp/TagLibrary.d (modified) (3 diffs)
- trunk/dsp/servlet/DSPRequest.d (modified) (2 diffs)
- trunk/dsp/servlet/DSPResponse.d (modified) (9 diffs)
- trunk/dsp/servlet/DSPServlet.d (modified) (1 diff)
- trunk/dsp/servlet/IDSPRequest.d (modified) (1 diff)
- trunk/dsp/servlet/IDSPResponse.d (modified) (1 diff)
- trunk/dsp/tags/Capture.d (modified) (2 diffs)
- trunk/dsp/tags/Servlet.d (modified) (1 diff)
- trunk/dspconf/index.dsp (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/doc/dsp-reference/tags.html
r103 r104 99 99 <p> 100 100 <ul> 101 <li><u>description</u> - describes the servlet (for debugging purposes)</li> 102 103 <li><u>name</u> - the name of the servlet. Used to distinguish between different servlets within the 101 <li><u>name</u> - <b>(string, required)</b> the name of the servlet. 102 Used to distinguish between different servlets within the 104 103 same file. (see the <a href='#tag'>tag tag</a> for more information)</li> 104 105 <!--li><u>description</u> - describes the servlet (for debugging purposes)</li--> 105 106 </ul> 106 107 </p> … … 115 116 <p> 116 117 The servlet tag defines information about the servlet. Multiple servlets are allowed per file, but 117 only one unnamed servlet is allowed. Only text inside the tag will be parsed as part of the servlet, 118 leaving the rest of the file before and after the tag to evaluate as plain text. In the case of 119 multiple servlets within the same file, text outside the servlet tags will evaluate as output 120 for the default servlet. 121 </p> 122 123 <h3>See Also:</h3> 124 <p> 125 <ul> 118 only one default (anonymous) servlet is allowed. 119 Only text inside the tag will be parsed as part of the servlet, 120 leaving the rest of the file before and after the tag to evaluate as the enclosing servlet, or the 121 default servlet if none is specified. 122 </p> 123 <p> 124 Named servlets can only be invoked via a custom tag binding, which is accomplished by use of the 125 <a href='#tag'>dsp:tag</a> tag. 126 </p> 127 128 <h3>See Also:</h3> 129 <p> 130 <ul> 131 <li><a href='#tag'>dsp:tag</a></li> 126 132 <li><a href='concepts.html#named-servlet'>Named Servlet</a></li> 127 133 <li><a href='concepts.html#default'>Default Servlet</a></li> … … 452 458 separated by a colon (e.g. "x:formatter:bold").</li> 453 459 460 <li><u>separator</u> - <b>(string)</b> a string used to distinguish between the name 461 (prefix) portion of a custom tag instance and the named-servlet is references (if applicable). 462 This defaults to ":". 463 454 464 <li><u>dependency</u> - <b>(string)</b> specifies if this generates an 455 465 <a href='concepts.html#dependencies'>external dependency</a> to the file indicated in 'file'. … … 477 487 'file' indicating a DSP servlet, the name attribute maps to the default servlet of the target. Should 478 488 the target contain any named servlets, those named servlets are also mapped to custom tags that use the 479 value of the 'name' attribute as a namespace, followed by a colonand the names of the named servlets.489 value of the 'name' attribute as a namespace, followed by the separator and the names of the named servlets. 480 490 This allows the developer to map to an entire 'tag library' by specifying a name like "x" so that the 481 491 named servlets in the 'tag library' are mapped similar to "x:foo" and "x:bar". trunk/dsp/DSPGrammar.d
r103 r104 41 41 public static char[] servletPrefix = "__servlet_"; 42 42 public static char[] servletDecl = "(IDSPRequest request, IDSPResponse response)"; 43 public static char[] outputVarDecl = "IWriter __writer = response.getWriter()";44 43 45 public static char[] writer = "__writer"; 46 public static char[] outputFunc = "__writer.put"; 44 public static char[] outputFunc = "response.getWriter.put"; 47 45 public static char[] replaceFunc = "std.string.replace"; 48 46 public static char[] toStringFunc = "std.string.toString"; … … 103 101 return "fromString(" ~ target ~ "," ~ source ~ ")"; 104 102 } 105 106 public static char[] resetWriter(){ 107 return writer ~ " = response.getWriter()"; 108 } 109 103 110 104 /* 111 105 public static char[] expressionEscape(char[] from,char[] to){ … … 118 112 } 119 113 120 public static char[] servletFunction(char[] registers,char[] name,char[] cpIn, char[] cpBody, char[] cpOut){ 121 return 122 "void " ~ servletFunctionName(name) ~ servletDecl ~ newline ~ 123 "in{" ~ outputVarDecl ~ ";" ~ newline ~ cpIn ~ "}" ~ newline ~ 124 "out{" ~ outputVarDecl ~ ";" ~ newline ~ cpOut ~ "}" ~ newline ~ 125 "body{" ~ outputVarDecl ~ ";" ~ newline ~ registers ~ newline ~ cpBody ~ "}" ~ newline 126 ; 127 } 128 129 public static char[] servletHeader(char[] name,char[] modulePath,char[][] servletImports){ 114 public static char[] servletHeader(char[] modulePath,char[][] servletImports){ 130 115 char[] imports = "import " ~ standardImports; 131 116 foreach(char[] imp; servletImports){ … … 137 122 "module " ~ pathToNamespace(modulePath) ~ ";" ~ newline ~ 138 123 imports ~ ";" ~ newline ~ 139 `alias void function` ~ servletDecl ~ ` ServletHandle; 124 `alias void delegate` ~ servletDecl ~ ` ServletHandle; 125 ServletHandle[char[]] __servletHandles; 140 126 141 127 extern(C) export void service(IDSPRequest request,IDSPResponse response){ 142 128 try{ 143 ` ~ servletFunctionName(name) ~`(request,response); 129 ServletHandle handle = __servletHandles[request.getServletName]; 130 if(handle) handle(request,response); 131 else response.setException("cannot locate servlet handle '" ~ request.getServletName ~ "'"); 144 132 } 145 133 catch(Exception e){ … … 149 137 response.setException("general servlet exception: " ~ o.toString()); 150 138 } 151 }` ~ newline139 }` 152 140 ; 141 } 142 143 public static char[] servletFunction(char[] registers,char[] name,char[] cpIn, char[] cpBody, char[] cpOut){ 144 return 145 "void " ~ servletFunctionName(name) ~ servletDecl ~ newline ~ 146 "in{" ~ cpIn ~ "}" ~ newline ~ 147 "out{" ~ cpOut ~ "}" ~ newline ~ 148 "body{" ~ newline ~ registers ~ newline ~ cpBody ~ "}" ~ newline ~ 149 "__servletHandles[" ~ runtimeString(name) ~ "] = &" ~ servletFunctionName(name) ~ ";" ~ newline ~ 150 newline; 151 } 152 153 public static char[] servletFooter(){ 154 return `} // end static this()`; 153 155 } 154 156 … … 156 158 return 157 159 "static char[][] __dependencies = [`" ~ modulePath ~ "`," ~ dependenciesList ~ "];" ~ newline ~ 158 "extern(C) export char[][] dependencies(){ return __dependencies; }" ~ newline 159 ;160 "extern(C) export char[][] dependencies(){ return __dependencies; }" ~ newline ~ 161 "static this(){" ~ newline ; 160 162 } 161 163 trunk/dsp/DSPProcessor.d
r103 r104 109 109 tags.startTag(context,name[DSPGrammar.preamble.length .. $],attribs,isEmpty); 110 110 } 111 else if(tags.isCustomTag(name)){112 tags.startCustomTag(context,name,attribs,isEmpty);113 }114 115 // just the raw preamble - this is a mistake116 else if(name == DSPGrammar.rawPreamble){117 throw new DSPException("invalid dsp tag '" ~ name ~ "' (forgot the rest?)");118 }119 120 // a plain XML/HTML tag - interpret the attributes and output it121 111 else{ 122 context.generator.appendOutput("<" ~ name); 123 124 foreach(char[] attribName,char[] attribValue; attribs){ 125 generator.appendOutput(" " ~ attribName ~ "=\'"); 126 generator.appendOutputReplace(attribValue,"\'","'"); 127 generator.appendOutput("\'"); 112 CustomTag tag = tags.getCustomTag(name); 113 if(!tag.isNull){ 114 tags.startCustomTag(tag,context,name,attribs,isEmpty); 115 } 116 // just the raw preamble - this is a mistake 117 else if(name == DSPGrammar.rawPreamble){ 118 throw new DSPException("invalid dsp tag '" ~ name ~ "' (forgot the rest?)"); 128 119 } 129 120 130 if(isEmpty){ 131 generator.appendOutput(" />"); 132 } 121 // a plain XML/HTML tag - interpret the attributes and output it 133 122 else{ 134 generator.appendOutput(">"); 123 context.generator.appendOutput("<" ~ name); 124 125 foreach(char[] attribName,char[] attribValue; attribs){ 126 generator.appendOutput(" " ~ attribName ~ "=\'"); 127 generator.appendOutputReplace(attribValue,"\'","'"); 128 generator.appendOutput("\'"); 129 } 130 131 if(isEmpty){ 132 generator.appendOutput(" />"); 133 } 134 else{ 135 generator.appendOutput(">"); 136 } 135 137 } 136 138 } … … 153 155 tags.endTag(context,name[DSPGrammar.preamble.length .. $],attribs,isEmpty); 154 156 } 155 else if(tags.isCustomTag(name)){ 156 tags.endCustomTag(context,name,attribs,isEmpty); 157 } 157 else{ 158 CustomTag tag = tags.getCustomTag(name); 159 if(!tag.isNull){ 160 tags.endCustomTag(tag,context,name,attribs,isEmpty); 161 } 158 162 159 // a plain XML/HTML tag - close it up 160 else if(!isEmpty){ 161 generator.appendOutput("</" ~ name ~ ">"); 163 // a plain XML/HTML tag - close it up 164 else if(!isEmpty){ 165 generator.appendOutput("</" ~ name ~ ">"); 166 } 162 167 } 163 168 } trunk/dsp/ServletCompilationContext.d
r99 r104 34 34 private import xml.utf8all; 35 35 36 structServletCompilationContext{37 RequestConfig requestConfig;38 DSPServletContext servletContext;39 DSPServlet servlet;40 XMLParser parser;41 ServletGenerator generator;42 TagLibrary tags;36 class ServletCompilationContext{ 37 public RequestConfig requestConfig; 38 public DSPServletContext servletContext; 39 public DSPServlet servlet; 40 public XMLParser parser; 41 public ServletGenerator generator; 42 public TagLibrary tags; 43 43 } trunk/dsp/ServletGenerator.d
r102 r104 51 51 unboxer!(Register) registerUnboxer; 52 52 unboxer!(char[]) stringUnboxer; 53 unboxer!(ServletGenerator) generatorUnboxer; 53 54 54 55 class ServletGenerator : RegisterSet{ … … 56 57 char[] servletCPOut; 57 58 char[] servletBody; 58 char[] servlet Name;59 char[] servletStatic; 59 60 char[] servletDependencies; 60 61 char[][] servletImports; … … 74 75 75 76 public this(){ 76 servletName = "default";77 77 contentMode = LiteralContentMode; 78 outputSegment = false; 78 79 } 79 80 … … 143 144 } 144 145 146 public void appendStatic(char[] value){ 147 servletStatic ~= value; 148 } 149 145 150 public void pushRegister(Register ptr){ 146 151 stack ~= box(ptr); … … 162 167 return ptr; 163 168 } 169 170 public void pushGenerator(ServletGenerator ptr){ 171 stack ~= box(ptr); 172 } 173 174 public ServletGenerator popGenerator(){ 175 ServletGenerator ptr = generatorUnboxer(stack[$-1]); 176 stack.length = stack.length - 1; 177 return ptr; 178 } 164 179 165 180 … … 249 264 public char[] getServletSource(char[] modulePath,char[] fileExtension){ 250 265 return 251 DSPGrammar.servletHeader( servletName,modulePath,servletImports) ~266 DSPGrammar.servletHeader(modulePath,servletImports) ~ 252 267 DSPGrammar.dependenciesSection(servletDependencies,"/" ~ modulePath ~ "." ~ fileExtension) ~ 253 DSPGrammar.servletFunction(renderRegisterSet(),servletName,servletCPIn,servletBody,servletCPOut) 268 DSPGrammar.servletFunction(renderRegisterSet(),"default",servletCPIn,servletBody,servletCPOut) ~ 269 "/** static start **/" ~ servletStatic ~ "/** static end **/" ~ 270 DSPGrammar.servletFooter(); 254 271 ; 255 272 } 273 274 public char[] getServletFunction(char[] servletName){ 275 return DSPGrammar.servletFunction(renderRegisterSet(),servletName,servletCPIn,servletBody,servletCPOut); 276 } 256 277 } trunk/dsp/TagLibrary.d
r103 r104 35 35 private import dsp.ServletCompilationContext; 36 36 37 struct CustomTag{ 38 char[] prefix; 39 char[] separator; 40 41 bit isNull(){ 42 return prefix is null; 43 } 44 uint size(){ 45 if(prefix is null) return 0; 46 return prefix.length + separator.length; 47 } 48 char[] servletName(char[] tagname){ 49 if(tagname.length <= size) return ""; 50 return tagname[size..$]; 51 } 52 } 53 37 54 class TagLibrary{ 38 55 DSPTag[char[]] tags; 39 char[][char[]] customTags;56 CustomTag[] customTags; 40 57 41 58 public this(){ … … 59 76 } 60 77 61 void addCustomTag(char[] name){ 62 customTags[name] = name; 78 void addCustomTag(char[] name,char[] sep){ 79 CustomTag tag; 80 tag.prefix = name; 81 tag.separator = sep; 82 83 customTags ~= tag; 63 84 } 64 85 65 bit isCustomTag(char[] name){ 66 return !((name in customTags) is null); 86 bit isTagDefined(char[] prefix){ 87 foreach(CustomTag tag; customTags){ 88 if(tag.prefix == prefix) return true; 89 } 90 return false; 91 } 92 93 // expensive search to find the right custom tag 94 CustomTag getCustomTag(char[] tagname){ 95 CustomTag bestMatch; 96 97 foreach(CustomTag tag; customTags){ 98 // too short, move on to the next one 99 if(tag.prefix > tagname) continue; 100 101 printf("%.*s == %.*s\n",tagname[0..tag.prefix.length],tag.prefix); 102 103 // do we start with the right prefix 104 if(tagname[0..tag.prefix.length] == tag.prefix){ 105 106 // exact match for the prefix: return immediately 107 if(tagname.length == tag.prefix.length){ 108 return tag; 109 } 110 111 // get a slice for the next segment to test 112 char[] substr = tagname[tag.prefix.length..$]; 113 114 // too short, move on to the next one 115 if(substr.length <= tag.separator.length) continue; 116 117 // not a match, move on 118 119 printf("%.*s == %.*s\n",substr[0..tag.separator.length],tag.separator); 120 121 if(substr[0..tag.separator.length] != tag.separator) continue; 122 123 // is this a better match than what we've found so far? 124 if(tag.size > bestMatch.size) bestMatch = tag; 125 } 126 } 127 return bestMatch; 67 128 } 68 129 69 public void startCustomTag( ServletCompilationContext context,char[] name,XMLAttributes attribs,bit isEmpty){130 public void startCustomTag(CustomTag tag,ServletCompilationContext context,char[] name,XMLAttributes attribs,bit isEmpty){ 70 131 with(context){ 71 generator.appendBody("response.startTag(" ~ DSPGrammar.runtimeString(name) ~ ");" ~ DSPGrammar.newline); 72 generator.appendBody(DSPGrammar.resetWriter ~ ";" ~ DSPGrammar.newline); 132 char[] servletName = tag.servletName(name); 133 char[] separator = servletName ? tag.separator : ""; 134 generator.appendBody("response.startTag(" ~ DSPGrammar.runtimeString(tag.prefix) ~ "," ~ DSPGrammar.runtimeString(separator) ~ "," ~ DSPGrammar.runtimeString(servletName) ~ ");" ~ DSPGrammar.newline); 73 135 74 136 foreach(char[] attrName,char[] value; attribs){ … … 81 143 } 82 144 83 public void endCustomTag(ServletCompilationContext context,char[] name,XMLAttributes attribs,bit isEmpty){ 84 context.generator.appendBody("response.endTag(request," ~ DSPGrammar.runtimeString(name) ~ "); " ~ DSPGrammar.resetWriter ~ ";" ~ DSPGrammar.newline); 145 public void endCustomTag(CustomTag tag,ServletCompilationContext context,char[] name,XMLAttributes attribs,bit isEmpty){ 146 char[] servletName = tag.servletName(name); 147 char[] separator = servletName ? tag.separator : ""; 148 149 context.generator.appendBody("response.endTag(request," ~ DSPGrammar.runtimeString(tag.prefix) ~ "," ~ DSPGrammar.runtimeString(separator) ~ "," ~ DSPGrammar.runtimeString(servletName) ~ ");" ~ DSPGrammar.newline); 85 150 } 86 151 } trunk/dsp/servlet/DSPRequest.d
r102 r104 39 39 DSPAttributes attribs; 40 40 char[] contentData; 41 char[] servletName; 41 42 42 43 public this(IProviderBridge bridge){ … … 47 48 attribs = null; 48 49 contentData = null; 50 servletName = "default"; 49 51 super.reset(); 50 52 } 51 53 54 public char[] getServletName(){ 55 return servletName; 56 } 57 58 public void setServletName(char[] name){ 59 if(!name || name.length == 0) servletName = "default"; 60 else servletName = name; 61 } 62 63 /+ 64 Uri getUri(){ 65 return super.getExplicitUri(); 66 } 67 68 char[] getPathInfo() 69 { 70 char[] path = super.getPathInfo(); 71 return path; 72 } 73 +/ 52 74 DSPAttributes attributes(){ 53 75 return attribs; trunk/dsp/servlet/DSPResponse.d
r103 r104 45 45 HttpWriter[] writerStack; 46 46 HttpWriter dspWriter; 47 RuntimeTag[char[]] [] customTagScopeStack;47 RuntimeTag[char[]] definedTags; 48 48 RuntimeTag[] tagStack; 49 49 RuntimeTag currentTagDecl; … … 52 52 public this(IProviderBridge bridge){ 53 53 super(bridge); 54 customTagScopeStack.length = 1;55 54 } 56 55 … … 58 57 writerStack.length = 0; 59 58 dspWriter = null; 60 customTagScopeStack.length = 0;61 customTagScopeStack.length = 1;62 59 tagStack.length = 0; 63 60 servlet = null; … … 109 106 110 107 private RuntimeTag findTag(char[] name){ 111 return customTagScopeStack[$-1][name];108 return definedTags[name]; 112 109 } 113 110 … … 124 121 tagStack.length = tagStack.length-1; 125 122 } 126 127 void startTagScope(){128 RuntimeTag[char[]] newScope;129 130 foreach(char[] name,RuntimeTag tag; customTagScopeStack[$-1]){131 newScope[name] = tag;132 }133 134 customTagScopeStack ~= newScope;135 }136 137 void endTagScope(){138 customTagScopeStack.length = customTagScopeStack.length-1;139 }140 123 141 124 void startTagDeclaration(IDSPRequest request,char[] tagname,char[] filename){ … … 144 127 tag.handle = servlet.getServletHandleForFile(request,filename); 145 128 146 customTagScopeStack[$-1][tagname] = tag;129 definedTags[tagname] = tag; 147 130 currentTagDecl = tag; 148 131 } … … 159 142 } 160 143 161 void startTag(char[] tagname){162 RuntimeTag tag = findTag( tagname);144 void startTag(char[] prefix,char[] sep,char[] servletName){ 145 RuntimeTag tag = findTag(prefix); 163 146 pushCurrentTag(tag); 164 147 … … 167 150 } 168 151 else{ 169 dspWriter.put("<" ~ tagname);152 dspWriter.put("<" ~ prefix ~ sep ~ servletName); 170 153 } 171 154 } … … 187 170 } 188 171 189 void endTag(IDSPRequest request,char[] tagname){172 void endTag(IDSPRequest request,char[] prefix,char[] sep,char[] servletName){ 190 173 RuntimeTag tag = getCurrentTag(); 191 174 if(!(tag is null)){ 192 175 DSPAttributes attributes = request.attributes; 193 176 char[] content = request.content; 177 char[] oldServletName = request.getServletName; 194 178 179 request.setServletName(servletName); 195 180 request.content = popWriter(); 196 181 request.attributes = tag.attributes; 197 182 tag.handle(request,this); 198 183 184 request.setServletName(oldServletName); 199 185 request.attributes = attributes; 200 186 request.content = content; 201 187 } 202 188 else{ 203 dspWriter.put("</" ~ tagname ~ ">");189 dspWriter.put("</" ~ prefix ~ sep ~ servletName ~ ">"); 204 190 } 205 191 popCurrentTag(); trunk/dsp/servlet/DSPServlet.d
r103 r104 141 141 debug printf("%.*s\n",cfg.toString()); 142 142 143 ServletCompilationContext scc ;143 ServletCompilationContext scc = new ServletCompilationContext(); 144 144 scc.servlet = this; 145 145 scc.requestConfig = cfg; trunk/dsp/servlet/IDSPRequest.d
r97 r104 31 31 32 32 interface IDSPRequest : IServletRequest{ 33 char[] getServletName(); 34 void setServletName(char[] name); 33 35 DSPAttributes attributes(); 34 36 DSPAttributes attributes(DSPAttributes attributes); trunk/dsp/servlet/IDSPResponse.d
r103 r104 46 46 void pushWriter(); 47 47 char[] popWriter(); 48 49 void startTagScope(); 50 void endTagScope(); 51 48 52 49 void startTagDeclaration(IDSPRequest request,char[] tagname,char[] filename); 53 50 void endTagDeclaration(); 54 51 void setTagDefault(char[] name,Box value); 55 52 56 void startTag(char[] tagname);53 void startTag(char[] prefix,char[] sep,char[] servletName); 57 54 void setTagAttribute(char[] name,Box value); 58 55 void endTagAttributes(); 59 void endTag(IDSPRequest request,char[] tagname);56 void endTag(IDSPRequest request,char[] prefix,char[] sep,char[] servletName); 60 57 } trunk/dsp/tags/Capture.d
r102 r104 44 44 with(context){ 45 45 generator.appendBody("response.pushWriter();" ~ DSPGrammar.newline); 46 generator.appendBody(DSPGrammar.writer ~ " = response.getWriter();" ~ DSPGrammar.newline);47 46 } 48 47 } … … 53 52 with(context){ 54 53 generator.appendBody(DSPGrammar.runtimeFromString(variable,"response.popWriter()") ~ ";" ~ DSPGrammar.newline); 55 generator.appendBody(DSPGrammar.writer ~ " = response.getWriter();" ~ DSPGrammar.newline);56 54 } 57 55 } trunk/dsp/tags/Servlet.d
r98 r104 32 32 private import dsp.DSPGrammar; 33 33 private import dsp.DSPTag; 34 private import dsp.DSPException; 34 35 private import dsp.ServletCompilationContext; 35 36 36 37 class Servlet : DSPTag{ 37 38 public void startTag(ServletCompilationContext context,char[] name,XMLAttributes attribs,bit isEmpty){ 38 //TODO: 39 with(context){ 40 //TODO: provide support for description 41 if(!attribs.contains("name")) throw new DSPException("attribute 'name' is required for dsp:servlet"); 42 43 char[] servletName = attribs["name"]; 44 if(name == "default") return; 45 46 ServletGenerator newGenerator = new ServletGenerator(); 47 newGenerator.pushGenerator(context.generator); 48 newGenerator.pushString(servletName); 49 50 context.generator = newGenerator; 51 } 39 52 } 40 53 41 54 public void endTag(ServletCompilationContext context,char[] name,XMLAttributes attribs,bit isEmpty){ 42 //TODO: 55 with(context){ 56 if(attribs["name"] == "default") return; 57 58 char[] servletName = generator.popString(); 59 ServletGenerator oldGenerator = generator.popGenerator(); 60 context.generator = oldGenerator; 61 62 oldGenerator.end(); 63 generator.appendStatic(oldGenerator.getServletFunction(servletName)); 64 } 43 65 } 44 66 } trunk/dspconf/index.dsp
r103 r104 5 5 <h2>DSP Tag unittests</h2> 6 6 <ul> 7 <li><a href='dsp_page.dsp'>dsp:page</a></li> 8 <li><a href='dsp_servlet.dsp'>dsp:servlet</a></li> 7 9 <li><a href='dsp_include.dsp'>dsp:include</a></li> 8 10 <li><a href='dsp_import.dsp'>dsp:import</a></li> … … 14 16 <li><a href='custom_tag.dsp'>Custom Tag</a></li> 15 17 </ul> 18 19 <?dsp 20 response.getWriter << request.getServletName(); 21 ?> 16 22 </body> 17 23 </html> 24 25 <dsp:servlet name="foo"> 26 <p><i>Pssst... its a secret</i></p> 27 </dsp:servlet>
