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 }