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