001 // Copyright 2004, 2005 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry.contrib.inspector; 016 017 import java.util.Iterator; 018 import java.util.Map; 019 020 import org.apache.tapestry.BaseComponent; 021 import org.apache.tapestry.IComponent; 022 import org.apache.tapestry.IDirect; 023 import org.apache.tapestry.IMarkupWriter; 024 import org.apache.tapestry.IRender; 025 import org.apache.tapestry.IRequestCycle; 026 import org.apache.tapestry.Tapestry; 027 import org.apache.tapestry.engine.DirectServiceParameter; 028 import org.apache.tapestry.engine.IEngineService; 029 import org.apache.tapestry.engine.ILink; 030 import org.apache.tapestry.parse.CloseToken; 031 import org.apache.tapestry.parse.ComponentTemplate; 032 import org.apache.tapestry.parse.LocalizationToken; 033 import org.apache.tapestry.parse.OpenToken; 034 import org.apache.tapestry.parse.TemplateToken; 035 import org.apache.tapestry.parse.TextToken; 036 import org.apache.tapestry.parse.TokenType; 037 import org.apache.tapestry.services.TemplateSource; 038 039 /** 040 * Component of the {@link Inspector}page used to display the ids and types of all embedded 041 * components. 042 * 043 * @author Howard Lewis Ship 044 */ 045 046 public abstract class ShowTemplate extends BaseComponent implements IDirect 047 { 048 /** @since 4.0 */ 049 public abstract TemplateSource getTemplateSource(); 050 051 public boolean getHasTemplate() 052 { 053 Inspector inspector; 054 055 inspector = (Inspector) getPage(); 056 057 // Components that inherit from BaseComponent have templates, 058 // others do not. 059 060 return inspector.getInspectedComponent() instanceof BaseComponent; 061 } 062 063 public IRender getTemplateDelegate() 064 { 065 return new IRender() 066 { 067 public void render(IMarkupWriter writer, IRequestCycle cycle) 068 { 069 writeTemplate(writer, cycle); 070 } 071 }; 072 } 073 074 /** 075 * Writes the HTML template for the component. When <jwc> tags are written, the id is made 076 * a link (that selects the named component). We use some magic to accomplish this, creating 077 * links as if we were a {@link DirectLink}component, and attributing those links to the 078 * captive {@link DirectLink}component embedded here. 079 */ 080 081 private void writeTemplate(IMarkupWriter writer, IRequestCycle cycle) 082 { 083 IComponent inspectedComponent = getInspectedComponent(); 084 ComponentTemplate template = null; 085 086 try 087 { 088 template = getTemplateSource().getTemplate(cycle, inspectedComponent); 089 } 090 catch (Exception ex) 091 { 092 return; 093 } 094 095 writer.begin("pre"); 096 097 int count = template.getTokenCount(); 098 099 for (int i = 0; i < count; i++) 100 { 101 TemplateToken token = template.getToken(i); 102 TokenType type = token.getType(); 103 104 if (type == TokenType.TEXT) 105 { 106 write(writer, (TextToken) token); 107 continue; 108 } 109 110 if (type == TokenType.CLOSE) 111 { 112 write(writer, (CloseToken) token); 113 114 continue; 115 } 116 117 if (token.getType() == TokenType.LOCALIZATION) 118 { 119 120 write(writer, (LocalizationToken) token); 121 continue; 122 } 123 124 if (token.getType() == TokenType.OPEN) 125 { 126 boolean nextIsClose = (i + 1 < count) 127 && (template.getToken(i + 1).getType() == TokenType.CLOSE); 128 129 write(writer, nextIsClose, (OpenToken) token); 130 131 if (nextIsClose) 132 i++; 133 134 continue; 135 } 136 137 // That's all the types known at this time. 138 } 139 140 writer.end(); // <pre> 141 } 142 143 /** @since 3.0 * */ 144 145 private IComponent getInspectedComponent() 146 { 147 Inspector page = (Inspector) getPage(); 148 149 return page.getInspectedComponent(); 150 } 151 152 /** @since 3.0 * */ 153 154 private void write(IMarkupWriter writer, TextToken token) 155 { 156 // Print the section of the template ... print() will 157 // escape and invalid characters as HTML entities. Also, 158 // we show the full stretch of text, not the trimmed version. 159 160 writer.print(token.getTemplateDataAsString()); 161 } 162 163 /** @since 3.0 * */ 164 165 private void write(IMarkupWriter writer, CloseToken token) 166 { 167 writer.begin("span"); 168 writer.attribute("class", "jwc-tag"); 169 170 writer.print("</"); 171 writer.print(token.getTag()); 172 writer.print(">"); 173 174 writer.end(); // <span> 175 } 176 177 /** @since 3.0 * */ 178 179 private void write(IMarkupWriter writer, LocalizationToken token) 180 { 181 IComponent component = getInspectedComponent(); 182 183 writer.begin("span"); 184 writer.attribute("class", "jwc-tag"); 185 186 writer.print("<span key=\""); 187 writer.print(token.getKey()); 188 writer.print('"'); 189 190 Map attributes = token.getAttributes(); 191 if (attributes != null && !attributes.isEmpty()) 192 { 193 Iterator it = attributes.entrySet().iterator(); 194 while (it.hasNext()) 195 { 196 Map.Entry entry = (Map.Entry) it.next(); 197 String attributeName = (String) entry.getKey(); 198 String attributeValue = (String) entry.getValue(); 199 200 writer.print(' '); 201 writer.print(attributeName); 202 writer.print("=\""); 203 writer.print(attributeValue); 204 writer.print('"'); 205 206 } 207 } 208 209 writer.print('>'); 210 writer.begin("span"); 211 writer.attribute("class", "localized-string"); 212 213 writer.print(component.getMessages().getMessage(token.getKey())); 214 writer.end(); // <span> 215 216 writer.print("</span>"); 217 218 writer.end(); // <span> 219 } 220 221 /** @since 3.0 * */ 222 223 private void write(IMarkupWriter writer, boolean nextIsClose, OpenToken token) 224 { 225 IComponent component = getInspectedComponent(); 226 IEngineService service = getPage().getEngine().getService(Tapestry.DIRECT_SERVICE); 227 228 // Each id references a component embedded in the inspected component. 229 // Get that component. 230 231 String id = token.getId(); 232 IComponent embedded = component.getComponent(id); 233 Object[] serviceParameters = new Object[] 234 { embedded.getIdPath() }; 235 236 // Build a URL to select that component, as if by the captive 237 // component itself (it's a Direct). 238 239 DirectServiceParameter dsp = new DirectServiceParameter(this, serviceParameters); 240 ILink link = service.getLink(false, dsp); 241 242 writer.begin("span"); 243 writer.attribute("class", "jwc-tag"); 244 245 writer.print("<"); 246 writer.print(token.getTag()); 247 248 writer.print(" jwcid=\""); 249 250 writer.begin("span"); 251 writer.attribute("class", "jwc-id"); 252 253 writer.begin("a"); 254 writer.attribute("href", link.getURL()); 255 writer.print(id); 256 257 writer.end(); // <a> 258 writer.end(); // <span> 259 writer.print('"'); 260 261 Map attributes = token.getAttributesMap(); 262 263 if (attributes != null) 264 { 265 Iterator ii = attributes.entrySet().iterator(); 266 267 while (ii.hasNext()) 268 { 269 Map.Entry e = (Map.Entry) ii.next(); 270 271 String value = (String) e.getValue(); 272 273 writer.print(' '); 274 writer.print(e.getKey().toString()); 275 writer.print("=\""); 276 writer.print(value); 277 writer.print('"'); 278 } 279 } 280 281 // Collapse an open & close down to a single tag. 282 283 if (nextIsClose) 284 writer.print('/'); 285 286 writer.print('>'); 287 writer.end(); // <span> 288 } 289 290 /** 291 * Invoked when a component id is clicked. 292 */ 293 294 public void trigger(IRequestCycle cycle) 295 { 296 Inspector inspector = (Inspector) getPage(); 297 298 String componentId = (String) cycle.getListenerParameters()[0]; 299 inspector.selectComponent(componentId); 300 301 IComponent newComponent = inspector.getInspectedComponent(); 302 303 // If the component is not a BaseComponent then it won't have 304 // a template, so switch to the specification view. 305 306 if (!(newComponent instanceof BaseComponent)) 307 inspector.setView(View.SPECIFICATION); 308 } 309 310 /** 311 * Always returns true. 312 * 313 * @since 2.3 314 */ 315 316 public boolean isStateful() 317 { 318 return true; 319 } 320 }